Class: Docker::Compose::Session

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

Overview

A Ruby OOP interface to a docker-compose session. A session is bound to a particular directory and docker-compose file (which are set at initialize time) and invokes whichever docker-compose command is resident in $PATH.

Run docker-compose commands by calling instance methods of this class and passing kwargs that are equivalent to the CLI options you would pass to the command-line tool.

Note that the Ruby command methods usually expose a subset of the options allowed by the docker-compose CLI, and that options are sometimes renamed for clarity, e.g. the “-d” flag always becomes the “detached:” kwarg.

Constant Summary collapse

ANSI =

A Regex that matches all ANSI escape sequences.

/\033\[([0-9];?)+[a-z]/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(shell = Backticks::Runner.new(buffered: [:stderr], interactive: true), dir: Dir.pwd, file: 'docker-compose.yml') ⇒ Session



27
28
29
30
31
32
# File 'lib/docker/compose/session.rb', line 27

def initialize(shell = Backticks::Runner.new(buffered: [:stderr], interactive: true),
               dir: Dir.pwd, file: 'docker-compose.yml')
  @shell = shell
  @dir = dir
  @file = file
end

Instance Attribute Details

#dirObject (readonly)

Working directory (determines compose project name); default is Dir.pwd



22
23
24
# File 'lib/docker/compose/session.rb', line 22

def dir
  @dir
end

#fileObject (readonly)

Project file; default is ‘docker-compose.yml’



25
26
27
# File 'lib/docker/compose/session.rb', line 25

def file
  @file
end

Instance Method Details

#build(*services, force_rm: false, no_cache: false, pull: false) ⇒ Object



232
233
234
235
236
237
# File 'lib/docker/compose/session.rb', line 232

def build(*services, force_rm: false, no_cache: false, pull: false)
  o = opts(force_rm: [force_rm, false],
           no_cache: [no_cache, false],
           pull: [pull, false])
  result = run!('build', o, services)
end

#config(*args) ⇒ Hash

Validate docker-compose file and return it as Hash

Raises:

  • (Error)

    if command fails



37
38
39
40
# File 'lib/docker/compose/session.rb', line 37

def config(*args)
  config = strip_ansi(run!('config', *args))
  YAML.load(config)
end

#down(remove_volumes: false) ⇒ Object

Take the stack down



108
109
110
# File 'lib/docker/compose/session.rb', line 108

def down(remove_volumes: false)
  run!('down', opts(v: [!!remove_volumes, false]))
end

#kill(*services, signal: 'KILL') ⇒ Object

Forcibly stop running services.

See Also:

  • for a list of acceptable signal names


173
174
175
176
# File 'lib/docker/compose/session.rb', line 173

def kill(*services, signal: 'KILL')
  o = opts(signal: [signal, 'KILL'])
  run!('kill', o, services)
end

#logs(*services) ⇒ true

Monitor the logs of one or more containers.

Raises:

  • (Error)

    if command fails



46
47
48
49
# File 'lib/docker/compose/session.rb', line 46

def logs(*services)
  run!('logs', services)
  true
end

#pause(*services) ⇒ Object

Pause running services.



150
151
152
# File 'lib/docker/compose/session.rb', line 150

def pause(*services)
  run!('pause', *services)
end

#port(service, port, protocol: 'tcp', index: 1) ⇒ String?

Figure out which interface(s) and port a given service port has been published to.

NOTE: if Docker Compose is communicating with a remote Docker host, this method returns IP addresses from the point of view of that host and its interfaces. If you need to know the address as reachable from localhost, you probably want to use ‘Mapper`.

Raises:

  • (Error)

    if command fails

See Also:



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/docker/compose/session.rb', line 193

def port(service, port, protocol: 'tcp', index: 1)
  inter = @shell.interactive
  @shell.interactive = false

  o = opts(protocol: [protocol, 'tcp'], index: [index, 1])
  s = strip_ansi(run!('port', o, service, port).strip)
  (!s.empty? && s) || nil
rescue Error => e
  # Deal with docker-compose v1.11+
  if e.detail =~ /No container found/i
    nil
  else
    raise
  end
ensure
  @shell.interactive = inter
end

#ps(*services) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/docker/compose/session.rb', line 51

def ps(*services)
  inter = @shell.interactive
  @shell.interactive = false

  lines = strip_ansi(run!('ps', {q: true}, services)).split(/[\r\n]+/)
  containers = Collection.new

  lines.each do |id|
    containers << docker_ps(strip_ansi(id))
  end

  containers
ensure
  @shell.interactive = inter
end

#pull(*services) ⇒ Object

Pull images of services



114
115
116
# File 'lib/docker/compose/session.rb', line 114

def pull(*services)
  run!('pull', *services)
end

#restart(*services, timeout: 10) ⇒ Object



143
144
145
146
# File 'lib/docker/compose/session.rb', line 143

def restart(*services, timeout:10)
  o = opts(timeout: [timeout, 10])
  run!('restart', o, *services)
end

#rm(*services, force: false, volumes: false) ⇒ Object



118
119
120
121
# File 'lib/docker/compose/session.rb', line 118

def rm(*services, force: false, volumes: false)
  o = opts(f: [force, false], v: [volumes, false])
  run!('rm', o, services)
end

#run(service, *cmd, detached: false, no_deps: false, volumes: [], env: [], rm: false, no_tty: false, user: nil) ⇒ Object

Idempotently run an arbitrary command with a service container.

Raises:

  • (Error)

    if command fails



136
137
138
139
140
141
# File 'lib/docker/compose/session.rb', line 136

def run(service, *cmd, detached: false, no_deps: false, volumes: [], env: [], rm: false, no_tty: false, user: nil)
  o = opts(d: [detached, false], no_deps: [no_deps, false], rm: [rm, false], T: [no_tty, false], u: [user, nil])
  env_params = env.map { |v| { e: v } }
  volume_params = volumes.map { |v| { v: v } }
  run!('run', o, *env_params, *volume_params, service, cmd)
end

#run!(*args) ⇒ String

Run a docker-compose command without validating that the CLI parameters make sense. Prepend project and file options if suitable.

Raises:

  • (Error)

    if command fails

See Also:

  • Docker::Compose::Shell#command


248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/docker/compose/session.rb', line 248

def run!(*args)
  file_args = case @file
  when 'docker-compose.yml'
    []
  when Array
    # backticks sugar can't handle array values; build a list of hashes
    # IMPORTANT: preserve the order of the files so overrides work correctly
    file_args = @file.map { |filepath| { :file => filepath } }
  else
    # a single String (or Pathname, etc); use normal sugar to add it
    [{ file: @file.to_s }]
  end

  @shell.chdir = dir
  cmd = @shell.run('docker-compose', *file_args, *args).join
  status = cmd.status
  out = cmd.captured_output
  err = cmd.captured_error
  status.success? || fail(Error.new(args.first, status, out+err))
  out
end

#scale(container_count, timeout: 10) ⇒ Object

Idempotently scales the number of containers for given services in the project.



101
102
103
104
105
# File 'lib/docker/compose/session.rb', line 101

def scale(container_count, timeout: 10)
  args = container_count.map {|service, count| "#{service}=#{count}"}
  o = opts(timeout: [timeout, 10])
  run!('scale', o, *args)
end

#stop(*services, timeout: 10) ⇒ Object

Stop running services.

Raises:

  • (Error)

    if command fails



164
165
166
167
# File 'lib/docker/compose/session.rb', line 164

def stop(*services, timeout: 10)
  o = opts(timeout: [timeout, 10])
  run!('stop', o, services)
end

#unpause(*services) ⇒ Object

Unpause running services.



156
157
158
# File 'lib/docker/compose/session.rb', line 156

def unpause(*services)
  run!('unpause', *services)
end

#up(*services, abort_on_container_exit: false, detached: false, timeout: 10, build: false, exit_code_from: nil, no_build: false, no_deps: false, no_start: false) ⇒ true

Idempotently up the given services in the project.

Raises:

  • (Error)

    if command fails



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/docker/compose/session.rb', line 79

def up(*services,
       abort_on_container_exit: false,
       detached: false, timeout: 10, build: false,
       exit_code_from: nil,
       no_build: false, no_deps: false, no_start: false)
  o = opts(
           abort_on_container_exit: [abort_on_container_exit, false],
           d: [detached, false],
           timeout: [timeout, 10],
           build: [build, false],
           exit_code_from: [exit_code_from, nil],
           no_build: [no_build, false],
           no_deps: [no_deps, false],
           no_start: [no_start, false]
  )
  run!('up', o, services)
  true
end

#version(short: false) ⇒ String, Hash

Determine the installed version of docker-compose.

Raises:

  • (Error)

    if command fails



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/docker/compose/session.rb', line 216

def version(short: false)
  o = opts(short: [short, false])
  result = run!('version', o, file: false, dir: false)

  if short
    result.strip
  else
    lines = result.split(/[\r\n]+/)
    lines.inject({}) do |h, line|
      kv = line.split(/: +/, 2)
      h[kv.first] = kv.last
      h
    end
  end
end