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 localhost, then maps URLs and other common network address formats so they point to the right host and port.

NOTE: this class uses some heuristics to deal with cases where the Docker client is talking to a remote server because the ‘DOCKER_HOST` environment variable is set. In those cases, Mapper tries to determine the IP address of the exposed services as reachable from localhost; it generally makes a correct guess, but in certain super-complex networking scenarios it may guess wrong. Please open a GitHub issue if you find a situation where Mapper provides a wrong answer.

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 = Session.new, net_info = NetInfo.new) ⇒ Mapper

Create an instance of Mapper

Parameters:



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/docker/compose/mapper.rb', line 48

def initialize(session=Session.new, net_info=NetInfo.new)
  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 docker-compose's
    # dirty lies!
    override_host = net_info.docker_routable_ip
  end

  @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:

  • env (Hash)

    a set of keys/values whose values will be mapped

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

Yields:

  • yields with each substituted (key, value) pair



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/docker/compose/mapper.rb', line 32

def self.map(env, session:Session.new, net_info:NetInfo.new)
  mapper = new(session, net_info)
  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



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/docker/compose/mapper.rb', line 102

def host_and_port(service, port)
  result = @session.port(service, port.to_s)
  if result
    result.chomp!
  else
    raise NoService,
          "Service '#{service}' not running, or does not " \
          "publish port '#{port}'"
  end

  host, port = result.split(':')
  host = @override_host if @override_host

  [host, Integer(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



88
89
90
91
92
93
94
# File 'lib/docker/compose/mapper.rb', line 88

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