Class: Docker::Compose::Mapper

Inherits:
Object
  • Object
show all
Defined in:
lib/docker/compose/mapper.rb

Overview

Uses a Session to discover information about services’ IP addresses and ports as reachable from the host, then

Constant Summary collapse

ELIDED =

Pattern that matches an “elided” host or port that should be omitted from output, but is needed to identify a specific container and port.

/^\[.+\]$/.freeze
REMOVE_ELIDED =

Regexp that can be used with gsub to strip elision marks

/[\[\]]/.freeze
BadSubstitution =
Class.new(StandardError)
NoService =
Class.new(RuntimeError)

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(session, override_host = nil) ⇒ Mapper

Create an instance of Mapper

Parameters:

  • session (Docker::Compose::Session)
  • override_host (String) (defaults to: nil)

    forcible address or DNS hostname to use; leave nil to trust docker-compose output.



50
51
52
53
# File 'lib/docker/compose/mapper.rb', line 50

def initialize(session, override_host = nil)
  @session = session
  @override_host = override_host
end

Class Method Details

.map(env, session: Session.new, net_info: NetInfo.new) { ... } ⇒ Object

Instantiate a mapper; map some environment variables; yield to caller for additional processing.

Parameters:

  • session (Session) (defaults to: Session.new)
  • net_info (NetInfo) (defaults to: NetInfo.new)

Yields:

  • yields with each substituted (key, value) pair



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/docker/compose/mapper.rb', line 22

def self.map(env, session:Session.new, net_info:NetInfo.new)
  # TODO: encapsulate this trickiness better ... inside NetInfo perhaps?
  docker_host = ENV['DOCKER_HOST']
  if docker_host.nil? || docker_host =~ /^(\/|unix|file)/
    # If DOCKER_HOST is blank, or pointing to a local socket, then we
    # can trust the address information returned by `docker-compose port`.
    override_host = nil
  else
    # If DOCKER_HOST is present, assume that containers have bound to
    # whatever IP we reach it at; don't fall victim to dirty NAT lies!
    override_host = net_info.docker_routable_ip
  end

  mapper = new(session, override_host)
  env.each_pair do |k, v|
    begin
      v = mapper.map(v)
      yield(k, v)
    rescue NoService
      yield(k, nil)
    end
  end
end

Instance Method Details

#host_and_port(service, port) ⇒ Array

Figure out which host port a given service’s port has been published to, and/or whether that service is running. Cannot distinguish between the “service not running” case and the “container port not published” case!

Returns:

  • (Array)

    (String, Integer) pair of host address and port number

Raises:

  • (NoService)

    if service is not up or does not publish port



92
93
94
95
96
97
98
99
# File 'lib/docker/compose/mapper.rb', line 92

def host_and_port(service, port)
  result = @session.port(service, port).chomp
  host, port = result.split(':')
  host = @override_host if @override_host
  [host, Integer(port)]
rescue RuntimeError, TypeError
  raise NoService, "Service '#{service}' not running, or does not publish port '#{port}'"
end

#map(value) ⇒ String, Array

Substitute service hostnames and ports that appear in a URL or a host:port string. If either component of a host:port string is surrounded by square brackets, “elide” that component, removing it from the result but using it to find the correct service and port.

Examples:

map MySQL on local docker host with 3306 published to 13847

map("tcp://db:3306") # => "tcp://127.0.0.1:13847"

map just the hostname of MySQL on local docker host

map("db:[3306]") # => "127.0.0.1"

map just the port of MySQL on local docker host

map("[db]:3306") # => "13847"

map an array of database hosts

map(["[db1]:3306", "[db2]:3306"])

Parameters:

  • value (String, #map)

    a URI, host:port pair, or an array of either

Returns:

  • (String, Array)

    the mapped value with container-names and ports substituted

Raises:

  • (BadSubstitution)

    if a substitution string can’t be parsed

  • (NoService)

    if service is not up or does not publish port



78
79
80
81
82
83
84
# File 'lib/docker/compose/mapper.rb', line 78

def map(value)
  if value.respond_to?(:map)
    value.map { |e| map_scalar(e) }
  else
    map_scalar(value)
  end
end