RabbitMQ consumer and publisher (Bunny/Ruby)

Tagged amqp, bunny, rabbitmq, ruby  Languages bash, ruby

Create consumer.rb:

require 'bunny'
bunny = Bunny.new(ENV.fetch('AMQP_URL'))
bunny.start
at_exit { bunny.stop }
channel = bunny.create_channel
channel.prefetch(1)
exchange = channel.topic(ENV.fetch('EXCHANGE'), durable: true, passive: true)
queue = channel.queue(ENV.fetch('QUEUE'), durable: true, passive: true)
queue.bind(exchange)
queue.subscribe(manual_ack: true, block: true) do |delivery_info, _metadata, payload|
  puts "==============="
  puts payload
  puts "==============="
  channel.acknowledge(delivery_info.delivery_tag, false)
end

Start consumer:

$ EXCHANGE=cnn QUEUE=news AMQP_URL=amqp://username:password@localhost:5672 ruby consumer.rb

Create publisher.rb:

require "bunny"

class Publisher
  def self.initialize(amqp_url, exchange:)
    @@bunny = Bunny.new(amqp_url)
    @@bunny.start
    @@channel = @@bunny.create_channel
    # 'passive = true' means exchange already exists
    @@exchange = @@bunny.channel.topic(exchange, passive: true)
    at_exit { @@bunny.stop }
  end

  def self.send(message, queue:)
    @@exchange.publish(message, routing_key: queue)
  end
end

queue = ENV.fetch('QUEUE')
Publisher.initialize(ENV.fetch('AMQP_URL'), exchange: ENV.fetch('EXCHANGE'))
Publisher.send('Ruby is the best programming language', queue: queue)
Publisher.send('Top 10 silver-bullet solutions in Java', queue: queue)

Start publisher.rb:

$ EXCHANGE=cnn QUEUE=news AMQP_URL=amqp://username:password@localhost:5672 ruby publisher.rb

Search and replace file contents and file names

Tagged bash, find, replace, search  Languages bash

Search and replace file contents and file names

Note that all changes must be commited to git before running the command:

git grep -l 'observation' | xargs sed -i '' -e 's/observation/condition/g'

Search and replace of file names

find . -name '*observation*' -exec bash -c 'mv $0 ${0/observation/condition}' {} \;

View Presenters Pattern in Rails

Tagged pattern, presenters, rails, presenter  Languages html, ruby

View (app/views/episodes/show.slim):

- ep = EpisodePresenter

ul
  li = ep.name(@episode)
  li = ep.producer_name(@episode)
  li = ep.director_name(@episode)
  li = ep.link_to(@episode, view: self)

BasePresenter (app/presenters/base_presenter.rb):

class BasePresenter
  #
  # Delegate methods to model
  #
  def self.method_missing(method, *args, &block)
    model = args.shift
    if model && model.respond_to?(method)
      model.send(method, *args, &block)
    else
      raise NoMethodError, method
    end
  end
end

Presenter (app/views/presenters/episode_presenters.rb):

class EpisodePresenter < BasePresenter
  #
  # NOTE: method missing delegates to the model, so no need to define this
  #
  # def self.name(episode)
  #   episode.name
  # end
  
  def self.producer_name(episode)
    "#{episode.producer.first_name} #{episode.producer.last_name}"
  end

  def self.director_name(episode)
    "#{episode.director.first_name} #{episode.director.last_name}"
  end
 
  #
  # NOTE: Rails helpers can be accessed through the view, if needed.
  #
  def self.link_to(episode, view:, classes: '', text: nil)
    attrs = {
      href: view.episode_path(episode),
      class: classes
    }
    view.content_tag :a, attrs do
      text.presence || 'View'
    end
  end
end

How to view the client certificates accepted by a website

Tagged ca, certificates, client certificate, openssl, ssl  Languages bash

To view a list of acceptable client certificates, execute:

$ openssl s_client -connect golang.org:443

The output is a list of acceptable CA names:

... 
---
Acceptable client certificate CA names
/C=FI/ST=Finland/O=Vaestorekisterikeskus CA/OU=XYZ/CN=ÅÄÖ
---
... 

or, if no client certificates are accepted:

... 
---
No client certificate CA names sent
---
... 

To log client certificate information with haproxy:

  bind *:443 ssl crt xyz.com.pem ca-file vrk-ca.pem verify optional crt-ignore-err all crl-file vrk-revocation-list.pem
  # See https://www.haproxy.com/blog/ssl-client-certificate-information-in-http-headers-and-logs/
  http-request set-header X-SSL                  %[ssl_fc]
  http-request set-header X-SSL-Client-Verify    %[ssl_c_verify]
  http-request set-header X-SSL-Client-DN        %{+Q}[ssl_c_s_dn]
  http-request set-header X-SSL-Client-CN        %{+Q}[ssl_c_s_dn(cn)]
  http-request set-header X-SSL-Issuer           %{+Q}[ssl_c_i_dn]
  http-request set-header X-SSL-Client-NotBefore %{+Q}[ssl_c_notbefore]
  http-request set-header X-SSL-Client-NotAfter  %{+Q}[ssl_c_notafter]

  log-format "%ci:%cp [%t] %ft %b/%s %Tq/%Tw/%Tc/%Tr/%Tt %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs {%[ssl_c_verify],%{+Q}[ssl_c_s_dn],%{+Q}[ssl_c_i_dn]} %{+Q}r"

Also see https://tools.ietf.org/html/rfc5246#page-53

How to start a bash shell in Docker

Tagged bash, docker, shell  Languages bash

To start a bash shell in an already running container:

docker exec -it <container id/name> bash

To start a bash shell using an image of a container that is not running:

docker run -i -t --entrypoint /bin/bash <image id>

How read process exit code in Golang

Tagged code, exit, status  Languages go

This function returns true if the nz command returns exit code 0:

package main

import (
    "os/exec"
    "strconv"
)

// IsPortOpen returns a boolean that indicates whether a port is open or closed.
func IsPortOpen(host string, port int) bool {
    args := []string{"-z", host, strconv.Itoa(port), "-G", "5"}
    cmd := exec.Command("/usr/bin/nc", args...)
    _, err := cmd.Output()
    if werr, ok := err.(*exec.ExitError); ok {
        if s := werr.Error(); s != "0" {
            return false
        }
    }
    return true
}

bcrypt example in Golang

Tagged bcrypt, golang  Languages go
package main

import (
    "golang.org/x/crypto/bcrypt"
    "log"
    "os"
)

// NOTE: You need to escape the dollar signs in the hash with \
// HASH="xxx" PASSWORD=xxx go run main.go
func main() {
    hash, ok := os.LookupEnv("HASH")
    if !ok {
        log.Fatal("The HASH environment variable is not set")
    }
    password, ok := os.LookupEnv("PASSWORD")
    if !ok {
        log.Fatal("The PASSWORD environment variable is not set")
    }
    if isMatch(password, hash) {
        log.Printf("Match '%s' == '%s'", hash, password)
    } else {
        log.Printf("Mismatch '%s' != '%s'", hash, password)
    }
}

func isMatch(password string, hash string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}

For loop in Postgres

Tagged for, loop, postgres  Languages sql
DO
$$
DECLARE
    row record;
BEGIN
    FOR row IN SELECT tablename FROM pg_tables WHERE schemaname = 'public'
    LOOP
        EXECUTE 'ALTER TABLE public.' || quote_ident(row.tablename) || ' SET SCHEMA <new schema>;';
    END LOOP;
END;
$$;