docker-compose 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.ruby-version +1 -1
- data/.travis.yml +1 -5
- data/Gemfile +1 -9
- data/README.md +44 -60
- data/Rakefile +3 -4
- data/bin/console +11 -15
- data/docker-compose.gemspec +12 -16
- data/lib/docker/compose/future/session.rb +90 -0
- data/lib/docker/compose/mapper.rb +35 -99
- data/lib/docker/compose/net_info.rb +5 -6
- data/lib/docker/compose/rake_tasks.rb +98 -111
- data/lib/docker/compose/session.rb +39 -280
- data/lib/docker/compose/shell.rb +165 -0
- data/lib/docker/compose/version.rb +1 -2
- data/lib/docker/compose.rb +2 -7
- metadata +9 -34
- data/.coveralls.yml +0 -1
- data/.github/workflows/publish.yml +0 -42
- data/.rubocop.yml +0 -38
- data/CHANGELOG.md +0 -32
- data/docker-compose.yml +0 -11
- data/lib/docker/compose/collection.rb +0 -13
- data/lib/docker/compose/container.rb +0 -80
- data/lib/docker/compose/error.rb +0 -26
- data/lib/docker/compose/shell_printer/fish.rb +0 -17
- data/lib/docker/compose/shell_printer/posix.rb +0 -33
- data/lib/docker/compose/shell_printer.rb +0 -26
@@ -1,14 +1,8 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'json'
|
3
1
|
require 'rake/tasklib'
|
4
|
-
require 'shellwords'
|
5
2
|
|
6
3
|
# In case this file is required directly
|
7
4
|
require 'docker/compose'
|
8
5
|
|
9
|
-
# Only used here, so only required here
|
10
|
-
require 'docker/compose/shell_printer'
|
11
|
-
|
12
6
|
module Docker::Compose
|
13
7
|
class RakeTasks < Rake::TaskLib
|
14
8
|
# Set the directory in which docker-compose commands will be run. Default
|
@@ -17,155 +11,148 @@ module Docker::Compose
|
|
17
11
|
# @return [String]
|
18
12
|
attr_accessor :dir
|
19
13
|
|
20
|
-
# Set the project name. Default is not to pass a custom name.
|
21
|
-
# @return [String]
|
22
|
-
attr_accessor :project_name
|
23
|
-
|
24
14
|
# Set the name of the docker-compose file. Default is`docker-compose.yml`.
|
25
15
|
# @return [String]
|
26
16
|
attr_accessor :file
|
27
17
|
|
28
|
-
# Provide a mapping of environment variables that should be set in
|
29
|
-
# _host_
|
30
|
-
# docker:compose:host.
|
31
|
-
#
|
18
|
+
# Provide a mapping of environment variables that should be set in the
|
19
|
+
# _host_ shell for docker:compose:env or docker:compose:server.
|
32
20
|
# The values of the environment variables can refer to names of services
|
33
|
-
# and ports defined in the docker-compose file, and this gem will
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# inside containers.
|
21
|
+
# and ports defined in the docker-compose file, and this gem will query
|
22
|
+
# docker-compose to find out which host IP and port the services are
|
23
|
+
# reachable on. This allows components running on the host to connect to
|
24
|
+
# services running inside containers.
|
37
25
|
#
|
38
26
|
# @see Docker::Compose::Mapper for information about the substitution syntax
|
39
|
-
attr_accessor :
|
40
|
-
|
41
|
-
# Extra environment variables
|
42
|
-
# are set _in addition_ to
|
43
|
-
# and
|
27
|
+
attr_accessor :env
|
28
|
+
|
29
|
+
# Extra environment variables that should be set before invoking the command
|
30
|
+
# specified for docker:compose:server. These are set _in addition_ to env
|
31
|
+
# (and should be disjoint from env), and do not necessarily need to map the
|
32
|
+
# location of a container; they can be simple extra env values that are
|
33
|
+
# useful to change the server's behavior when it runs in cooperation
|
34
|
+
# with containers.
|
44
35
|
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
attr_accessor :
|
36
|
+
# If there is overlap between env and server_env, then keys of server_env
|
37
|
+
# will "win"; they are set last.
|
38
|
+
attr_accessor :server_env
|
48
39
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
|
53
|
-
# that will run on the host.
|
54
|
-
attr_accessor :host_services
|
55
|
-
|
56
|
-
# Namespace to define the rake tasks under. Defaults to "docker:compose'.
|
57
|
-
attr_accessor :rake_namespace
|
40
|
+
# Command to exec on the _host_ when someone invokes docker:compose:server.
|
41
|
+
# This is used to start up all containers and then run a server that
|
42
|
+
# depends on them and is properly linked to them.
|
43
|
+
attr_accessor :server
|
58
44
|
|
59
45
|
# Construct Rake wrapper tasks for docker-compose. If a block is given,
|
60
46
|
# yield self to the block before defining any tasks so their behavior
|
61
|
-
# can be configured by calling #
|
47
|
+
# can be configured by calling #env=, #file= and so forth.
|
62
48
|
def initialize
|
63
49
|
self.dir = Rake.application.original_dir
|
64
|
-
self.project_name = nil
|
65
50
|
self.file = 'docker-compose.yml'
|
66
|
-
self.
|
67
|
-
self.
|
68
|
-
self.rake_namespace = 'docker:compose'
|
51
|
+
self.env = {}
|
52
|
+
self.server_env = {}
|
69
53
|
yield self if block_given?
|
70
54
|
|
71
|
-
@shell =
|
72
|
-
@session = Docker::Compose::Session.new(@shell, dir:
|
55
|
+
@shell = Docker::Compose::Shell.new
|
56
|
+
@session = Docker::Compose::Session.new(@shell, dir:dir, file:file)
|
73
57
|
@net_info = Docker::Compose::NetInfo.new
|
74
|
-
@shell_printer = Docker::Compose::ShellPrinter.new
|
75
|
-
|
76
|
-
@shell.interactive = true
|
77
58
|
|
78
59
|
define
|
79
60
|
end
|
80
61
|
|
81
|
-
def define
|
82
|
-
namespace
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
62
|
+
private def define
|
63
|
+
namespace :docker do
|
64
|
+
namespace :compose do
|
65
|
+
desc 'Print bash exports with IP/ports of running services'
|
66
|
+
task :env do
|
67
|
+
@shell.interactive = false # suppress useless 'port' output
|
68
|
+
|
69
|
+
if Rake.application.top_level_tasks.include? 'docker:compose:env'
|
70
|
+
# This task is being run as top-level; print some bash export
|
71
|
+
# statements or usage information depending on whether STDOUT
|
72
|
+
# is a tty.
|
73
|
+
if STDOUT.tty?
|
74
|
+
print_usage
|
75
|
+
else
|
76
|
+
export_env(print:true)
|
77
|
+
end
|
78
|
+
else
|
79
|
+
# This task is a dependency of something else; just export the
|
80
|
+
# environment variables for use in-process by other Rake tasks.
|
81
|
+
export_env(print:false)
|
82
|
+
end
|
83
|
+
end
|
93
84
|
|
94
|
-
|
85
|
+
desc 'Launch services needed to run this application'
|
86
|
+
task :up do
|
87
|
+
@shell.interactive = true # let user see what's happening
|
88
|
+
@session.up(detached:true)
|
89
|
+
end
|
95
90
|
|
96
|
-
|
97
|
-
|
91
|
+
desc 'Tail logs of all running services'
|
92
|
+
task :logs do
|
93
|
+
@session.logs
|
94
|
+
end
|
98
95
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
@session.up(*host_services, detached: true)
|
103
|
-
else
|
104
|
-
@session.up(detached: true)
|
96
|
+
desc 'Stop services needed to run this application'
|
97
|
+
task :stop do
|
98
|
+
@session.stop
|
105
99
|
end
|
106
100
|
|
107
|
-
|
101
|
+
desc 'Run application on the host, linked to services in containers'
|
102
|
+
task :server => ['docker:compose:up', 'docker:compose:env'] do
|
103
|
+
exec(self.server)
|
104
|
+
end
|
108
105
|
end
|
109
106
|
end
|
110
107
|
end
|
111
|
-
private :define
|
112
108
|
|
113
109
|
# Substitute and set environment variables that point to network ports
|
114
110
|
# published by docker-compose services. Optionally also print bash export
|
115
111
|
# statements so this information can be made available to a user's shell.
|
116
|
-
def export_env(print:)
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
112
|
+
private def export_env(print:)
|
113
|
+
# First, do env substitutions in strict mode; don't catch BadSubstitution
|
114
|
+
# so the caller knows when he has a bogus value
|
115
|
+
mapper = Docker::Compose::Mapper.new(@session,
|
116
|
+
@net_info.docker_routable_ip)
|
117
|
+
self.env.each_pair do |k, v|
|
118
|
+
begin
|
119
|
+
v = mapper.map(v)
|
120
|
+
ENV[k] = v
|
121
|
+
print_env(k, v) if print
|
122
|
+
rescue Docker::Compose::Mapper::NoService
|
123
|
+
ENV[k] = nil
|
124
|
+
print_env(k, nil) if print
|
125
|
+
end
|
122
126
|
end
|
123
127
|
|
124
|
-
|
125
|
-
|
126
|
-
|
128
|
+
# Next, do server substitutions in non-strict mode since server_env
|
129
|
+
# can contain arbitrary values.
|
130
|
+
mapper = Docker::Compose::Mapper.new(@session,
|
131
|
+
@net_info.docker_routable_ip,
|
132
|
+
strict:false)
|
133
|
+
self.server_env.each_pair do |k, v|
|
134
|
+
v = mapper.map(v)
|
135
|
+
ENV[k] = v
|
136
|
+
print_env(k, v) if print
|
127
137
|
end
|
128
138
|
end
|
129
|
-
private :export_env
|
130
|
-
|
131
|
-
# Transform a Ruby value into a String that can be stored in the
|
132
|
-
# environment. This accepts nil, String, or Array and returns nil, String
|
133
|
-
# or JSON-serialized Array.
|
134
|
-
def serialize_for_env(v)
|
135
|
-
case v
|
136
|
-
when String
|
137
|
-
v
|
138
|
-
when NilClass
|
139
|
-
nil
|
140
|
-
when Array
|
141
|
-
JSON.dump(v)
|
142
|
-
else
|
143
|
-
fail ArgumentError, "Can't represent a #{v.class} in the environment"
|
144
|
-
end
|
145
|
-
end
|
146
|
-
private :serialize_for_env
|
147
139
|
|
148
|
-
# Print
|
149
|
-
def print_env(k, v)
|
140
|
+
# Print a bash export or unset statement
|
141
|
+
private def print_env(k, v)
|
150
142
|
if v
|
151
|
-
puts
|
143
|
+
puts format('export %s=%s', k, v)
|
152
144
|
else
|
153
|
-
puts
|
145
|
+
puts format('unset %s # service not running', k)
|
154
146
|
end
|
155
147
|
end
|
156
|
-
private :print_env
|
157
|
-
|
158
|
-
def print_usage
|
159
|
-
command = "rake #{task_name('env')}"
|
160
|
-
command = 'bundle exec ' + command if defined?(Bundler)
|
161
|
-
puts @shell_printer.comment('To export these variables to your shell, run:')
|
162
|
-
puts @shell_printer.comment(@shell_printer.eval_output(command))
|
163
|
-
end
|
164
|
-
private :print_usage
|
165
148
|
|
166
|
-
def
|
167
|
-
|
149
|
+
private def print_usage
|
150
|
+
be = 'bundle exec ' if defined?(Bundler)
|
151
|
+
puts "# To export container network locations to your environment:"
|
152
|
+
puts %Q{eval "$(#{be}rake docker:compose:env)"}
|
153
|
+
puts
|
154
|
+
puts '# To learn which environment variables we will export:'
|
155
|
+
puts %Q{echo "$(#{be}rake docker:compose:env)"}
|
168
156
|
end
|
169
|
-
private :task_name
|
170
157
|
end
|
171
158
|
end
|
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'backticks'
|
3
|
-
require 'yaml'
|
1
|
+
require 'docker/compose/future/session'
|
4
2
|
|
5
3
|
module Docker::Compose
|
6
4
|
# A Ruby OOP interface to a docker-compose session. A session is bound to
|
@@ -15,220 +13,69 @@ module Docker::Compose
|
|
15
13
|
# allowed by the docker-compose CLI, and that options are sometimes renamed
|
16
14
|
# for clarity, e.g. the "-d" flag always becomes the "detached:" kwarg.
|
17
15
|
class Session
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
# Working directory (determines compose project name); default is Dir.pwd
|
22
|
-
attr_reader :dir
|
23
|
-
|
24
|
-
# Project name; default is not to pass a custom name
|
25
|
-
attr_reader :project_name
|
26
|
-
|
27
|
-
# Project file; default is 'docker-compose.yml'
|
28
|
-
attr_reader :file
|
29
|
-
|
30
|
-
# Reference to the last executed command.
|
31
|
-
attr_reader :last_command
|
32
|
-
|
33
|
-
def initialize(shell = Backticks::Runner.new(buffered: [:stderr], interactive: true),
|
34
|
-
dir: Dir.pwd, project_name: nil, file: 'docker-compose.yml')
|
16
|
+
def initialize(shell=Docker::Compose::Shell.new,
|
17
|
+
dir:Dir.pwd, file:'docker-compose.yml')
|
35
18
|
@shell = shell
|
36
|
-
@project_name = project_name
|
37
19
|
@dir = dir
|
38
20
|
@file = file
|
39
|
-
@last_command = nil
|
40
|
-
end
|
41
|
-
|
42
|
-
# Validate docker-compose file and return it as Hash
|
43
|
-
# @return [Hash] the docker-compose config file
|
44
|
-
# @raise [Error] if command fails
|
45
|
-
def config(*args)
|
46
|
-
config = strip_ansi(run!('config', *args))
|
47
|
-
YAML.load(config)
|
48
21
|
end
|
49
22
|
|
50
23
|
# Monitor the logs of one or more containers.
|
51
24
|
# @param [Array] services list of String service names to show logs for
|
52
25
|
# @return [true] always returns true
|
53
|
-
# @raise [
|
26
|
+
# @raise [RuntimeError] if command fails
|
54
27
|
def logs(*services)
|
55
28
|
run!('logs', services)
|
56
29
|
true
|
57
30
|
end
|
58
31
|
|
59
|
-
|
60
|
-
inter = @shell.interactive
|
61
|
-
@shell.interactive = false
|
62
|
-
|
63
|
-
lines = strip_ansi(run!('ps', {q: true}, services)).split(/[\r\n]+/)
|
64
|
-
containers = Collection.new
|
65
|
-
|
66
|
-
lines.each do |id|
|
67
|
-
containers << docker_ps(strip_ansi(id))
|
68
|
-
end
|
69
|
-
|
70
|
-
containers
|
71
|
-
ensure
|
72
|
-
@shell.interactive = inter
|
73
|
-
end
|
74
|
-
|
75
|
-
# Idempotently up the given services in the project.
|
32
|
+
# Idempotently run services in the project,
|
76
33
|
# @param [Array] services list of String service names to run
|
77
34
|
# @param [Boolean] detached if true, to start services in the background;
|
78
35
|
# otherwise, monitor logs in the foreground and shutdown on Ctrl+C
|
79
|
-
# @param [Integer] timeout how long to wait for each service to
|
80
|
-
# @param [Boolean]
|
81
|
-
#
|
82
|
-
# missing
|
36
|
+
# @param [Integer] timeout how long to wait for each service to stostart
|
37
|
+
# @param [Boolean] no_build if true, to skip building images for services
|
38
|
+
# that have a `build:` instruction in the docker-compose file
|
83
39
|
# @param [Boolean] no_deps if true, just run specified services without
|
84
40
|
# running the services that they depend on
|
85
41
|
# @return [true] always returns true
|
86
|
-
# @raise [
|
42
|
+
# @raise [RuntimeError] if command fails
|
87
43
|
def up(*services,
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
o = opts(
|
93
|
-
abort_on_container_exit: [abort_on_container_exit, false],
|
94
|
-
d: [detached, false],
|
95
|
-
timeout: [timeout, 10],
|
96
|
-
build: [build, false],
|
97
|
-
exit_code_from: [exit_code_from, nil],
|
98
|
-
no_build: [no_build, false],
|
99
|
-
no_deps: [no_deps, false],
|
100
|
-
no_start: [no_start, false]
|
101
|
-
)
|
102
|
-
run!('up', o, services)
|
44
|
+
detached:false, timeout:10, no_build:false, no_deps:false)
|
45
|
+
run!('up',
|
46
|
+
{d:detached, timeout:timeout, no_build:no_build, no_deps:no_deps},
|
47
|
+
services)
|
103
48
|
true
|
104
49
|
end
|
105
50
|
|
106
|
-
# Idempotently scales the number of containers for given services in the project.
|
107
|
-
# @param [Hash] container_count per service, e.g. {web: 2, worker: 3}
|
108
|
-
# @param [Integer] timeout how long to wait for each service to scale
|
109
|
-
def scale(container_count, timeout: 10)
|
110
|
-
args = container_count.map {|service, count| "#{service}=#{count}"}
|
111
|
-
o = opts(timeout: [timeout, 10])
|
112
|
-
run!('scale', o, *args)
|
113
|
-
end
|
114
|
-
|
115
|
-
# Take the stack down
|
116
|
-
def down(remove_volumes: false)
|
117
|
-
run!('down', opts(v: [!!remove_volumes, false]))
|
118
|
-
end
|
119
|
-
|
120
|
-
# Pull images of services
|
121
|
-
# @param [Array] services list of String service names to pull
|
122
|
-
def pull(*services)
|
123
|
-
run!('pull', *services)
|
124
|
-
end
|
125
|
-
|
126
|
-
def rm(*services, force: false, volumes: false)
|
127
|
-
o = opts(f: [force, false], v: [volumes, false])
|
128
|
-
run!('rm', o, services)
|
129
|
-
end
|
130
|
-
|
131
|
-
# Idempotently run an arbitrary command with a service container.
|
132
|
-
# @param [String] service name to run
|
133
|
-
# @param [String] cmd command statement to run
|
134
|
-
# @param [Boolean] detached if true, to start services in the background;
|
135
|
-
# otherwise, monitor logs in the foreground and shutdown on Ctrl+C
|
136
|
-
# @param [Boolean] no_deps if true, just run specified services without
|
137
|
-
# running the services that they depend on
|
138
|
-
# @param [Array] env a list of environment variables (see: -e flag)
|
139
|
-
# @param [Array] volumes a list of volumes to bind mount (see: -v flag)
|
140
|
-
# @param [Boolean] rm remove the container when done
|
141
|
-
# @param [Boolean] no_tty disable pseudo-tty allocation (see: -T flag)
|
142
|
-
# @param [String] user run as specified username or uid (see: -u flag)
|
143
|
-
# @raise [Error] if command fails
|
144
|
-
def run(service, *cmd, detached: false, no_deps: false, volumes: [], env: [], rm: false, no_tty: false, user: nil, service_ports: false)
|
145
|
-
o = opts(d: [detached, false], no_deps: [no_deps, false], rm: [rm, false], T: [no_tty, false], u: [user, nil], service_ports: [service_ports, false])
|
146
|
-
env_params = env.map { |v| { e: v } }
|
147
|
-
volume_params = volumes.map { |v| { v: v } }
|
148
|
-
run!('run', o, *env_params, *volume_params, service, cmd)
|
149
|
-
end
|
150
|
-
|
151
|
-
def restart(*services, timeout:10)
|
152
|
-
o = opts(timeout: [timeout, 10])
|
153
|
-
run!('restart', o, *services)
|
154
|
-
end
|
155
|
-
|
156
|
-
# Pause running services.
|
157
|
-
# @param [Array] services list of String service names to run
|
158
|
-
def pause(*services)
|
159
|
-
run!('pause', *services)
|
160
|
-
end
|
161
|
-
|
162
|
-
# Unpause running services.
|
163
|
-
# @param [Array] services list of String service names to run
|
164
|
-
def unpause(*services)
|
165
|
-
run!('unpause', *services)
|
166
|
-
end
|
167
|
-
|
168
51
|
# Stop running services.
|
169
52
|
# @param [Array] services list of String service names to stop
|
170
53
|
# @param [Integer] timeout how long to wait for each service to stop
|
171
|
-
|
172
|
-
|
173
|
-
o = opts(timeout: [timeout, 10])
|
174
|
-
run!('stop', o, services)
|
175
|
-
end
|
176
|
-
|
177
|
-
# Forcibly stop running services.
|
178
|
-
# @param [Array] services list of String service names to stop
|
179
|
-
# @param [String] name of murderous signal to use, default is 'KILL'
|
180
|
-
# @see Signal.list for a list of acceptable signal names
|
181
|
-
def kill(*services, signal: 'KILL')
|
182
|
-
o = opts(signal: [signal, 'KILL'])
|
183
|
-
run!('kill', o, services)
|
54
|
+
def stop(*services, timeout:10)
|
55
|
+
run!('stop', {timeout:timeout}, services)
|
184
56
|
end
|
185
57
|
|
186
|
-
# Figure out which
|
187
|
-
#
|
188
|
-
# **NOTE**: if Docker Compose is communicating with a remote Docker host, this method
|
189
|
-
# returns IP addresses from the point of view of *that* host and its interfaces. If
|
190
|
-
# you need to know the address as reachable from localhost, you probably want to use
|
191
|
-
# `Mapper`.
|
192
|
-
#
|
193
|
-
# @see Docker::Compose::Mapper
|
194
|
-
#
|
58
|
+
# Figure out which host a port a given service port has been published to.
|
195
59
|
# @param [String] service name of service from docker-compose.yml
|
196
60
|
# @param [Integer] port number of port
|
197
61
|
# @param [String] protocol 'tcp' or 'udp'
|
198
62
|
# @param [Integer] index of container (if multiple instances running)
|
199
|
-
|
200
|
-
|
201
|
-
def port(service, port, protocol: 'tcp', index: 1)
|
202
|
-
inter = @shell.interactive
|
203
|
-
@shell.interactive = false
|
204
|
-
|
205
|
-
o = opts(protocol: [protocol, 'tcp'], index: [index, 1])
|
206
|
-
s = strip_ansi(run!('port', o, service, port).strip)
|
207
|
-
(!s.empty? && s) || nil
|
208
|
-
rescue Error => e
|
209
|
-
# Deal with docker-compose v1.11+
|
210
|
-
if e.detail =~ /No container found/i
|
211
|
-
nil
|
212
|
-
else
|
213
|
-
raise
|
214
|
-
end
|
215
|
-
ensure
|
216
|
-
@shell.interactive = inter
|
63
|
+
def port(service, port, protocol:'tcp', index:1)
|
64
|
+
run!('port', {protocol:protocol, index:index}, service, port)
|
217
65
|
end
|
218
66
|
|
219
67
|
# Determine the installed version of docker-compose.
|
220
68
|
# @param [Boolean] short whether to return terse version information
|
221
69
|
# @return [String, Hash] if short==true, returns a version string;
|
222
|
-
# otherwise, returns a Hash of component
|
223
|
-
# @raise [
|
224
|
-
def version(short:
|
225
|
-
|
226
|
-
result = run!('version', o, file: false, dir: false)
|
70
|
+
# otherwise, returns a Hash of component names to version strings
|
71
|
+
# @raise [RuntimeError] if command fails
|
72
|
+
def version(short:false)
|
73
|
+
result = run!('version', short:short, file:false, dir:false)
|
227
74
|
|
228
75
|
if short
|
229
76
|
result.strip
|
230
77
|
else
|
231
|
-
lines = result.split(
|
78
|
+
lines = result.split("\n")
|
232
79
|
lines.inject({}) do |h, line|
|
233
80
|
kv = line.split(/: +/, 2)
|
234
81
|
h[kv.first] = kv.last
|
@@ -237,118 +84,30 @@ module Docker::Compose
|
|
237
84
|
end
|
238
85
|
end
|
239
86
|
|
240
|
-
def build(*services, force_rm: false, no_cache: false, pull: false)
|
241
|
-
o = opts(force_rm: [force_rm, false],
|
242
|
-
no_cache: [no_cache, false],
|
243
|
-
pull: [pull, false])
|
244
|
-
result = run!('build', o, services)
|
245
|
-
end
|
246
|
-
|
247
87
|
# Run a docker-compose command without validating that the CLI parameters
|
248
88
|
# make sense. Prepend project and file options if suitable.
|
249
89
|
#
|
250
90
|
# @see Docker::Compose::Shell#command
|
251
91
|
#
|
252
|
-
# @param [Array]
|
253
|
-
#
|
92
|
+
# @param [Array] cmd subcommand words and options in the format accepted by
|
93
|
+
# Shell#command
|
254
94
|
# @return [String] output of the command
|
255
|
-
# @raise [
|
256
|
-
def run!(*
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
# IMPORTANT: preserve the order of the files so overrides work correctly
|
268
|
-
file_args = @file.map { |filepath| { :file => filepath } }
|
269
|
-
else
|
270
|
-
# a single String (or Pathname, etc); use normal sugar to add it
|
271
|
-
[{ file: @file.to_s }]
|
95
|
+
# @raise [RuntimeError] if command fails
|
96
|
+
def run!(*cmd)
|
97
|
+
project_opts = {
|
98
|
+
file: @file
|
99
|
+
}
|
100
|
+
|
101
|
+
Dir.chdir(@dir) do
|
102
|
+
result, output =
|
103
|
+
@shell.command('docker-compose', project_opts, *cmd)
|
104
|
+
(result == 0) || raise(RuntimeError,
|
105
|
+
"#{cmd.first} failed with status #{result}")
|
106
|
+
output
|
272
107
|
end
|
273
|
-
|
274
|
-
@shell.chdir = dir
|
275
|
-
@last_command = @shell.run('docker-compose', *project_name_args, *file_args, *args).join
|
276
|
-
status = @last_command.status
|
277
|
-
out = @last_command.captured_output
|
278
|
-
err = @last_command.captured_error
|
279
|
-
status.success? || fail(Error.new(args.first, status, out+err))
|
280
|
-
out
|
281
|
-
end
|
282
|
-
|
283
|
-
private
|
284
|
-
|
285
|
-
def docker_ps(id)
|
286
|
-
cmd = @shell.run('docker', 'ps', a: true, f: "id=#{id}", no_trunc: true, format: Container::PS_FMT).join
|
287
|
-
status, out, err = cmd.status, cmd.captured_output, cmd.captured_error
|
288
|
-
raise Error.new('docker ps', status, out+err) unless status.success?
|
289
|
-
lines = out.split(/[\r\n]+/)
|
290
|
-
return nil if lines.empty?
|
291
|
-
l = strip_ansi(lines.shift)
|
292
|
-
m = parse(l)
|
293
|
-
raise Error.new('docker ps', status, "Cannot parse output: '#{l}'") unless m
|
294
|
-
raise Error.new('docker ps', status, "Cannot parse output: '#{l}'") unless m.size == 7
|
295
|
-
return Container.new(*m)
|
296
|
-
end
|
297
|
-
|
298
|
-
# strip default-valued options. the value of each kw should be a pair:
|
299
|
-
# [0] is present value
|
300
|
-
# [1] is default value
|
301
|
-
def opts(**kws)
|
302
|
-
res = {}
|
303
|
-
kws.each_pair do |kw, v|
|
304
|
-
res[kw] = v[0] unless v[0] == v[1]
|
305
|
-
end
|
306
|
-
res
|
307
|
-
end
|
308
|
-
|
309
|
-
# strip all ANSI escape sequences from str
|
310
|
-
def strip_ansi(str)
|
311
|
-
str.gsub(ANSI, '')
|
312
108
|
end
|
313
109
|
|
314
|
-
#
|
315
|
-
|
316
|
-
#
|
317
|
-
# @param [String] str e.g. "(foo) ((bar)) ... (baz)"
|
318
|
-
# @return [Array] e.g. ["foo", "bar", "baz"]
|
319
|
-
def parse(str)
|
320
|
-
fields = []
|
321
|
-
nest = 0
|
322
|
-
field = ''
|
323
|
-
str.each_char do |ch|
|
324
|
-
got = false
|
325
|
-
if nest == 0
|
326
|
-
if ch == '('
|
327
|
-
nest += 1
|
328
|
-
end
|
329
|
-
else
|
330
|
-
if ch == '('
|
331
|
-
nest += 1
|
332
|
-
field << ch
|
333
|
-
elsif ch == ')'
|
334
|
-
nest -= 1
|
335
|
-
if nest == 0
|
336
|
-
got = true
|
337
|
-
else
|
338
|
-
field << ch
|
339
|
-
end
|
340
|
-
else
|
341
|
-
field << ch
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
if got
|
346
|
-
fields << field
|
347
|
-
field = ''
|
348
|
-
end
|
349
|
-
end
|
350
|
-
|
351
|
-
fields
|
352
|
-
end
|
110
|
+
# Simulate behaviors from Docker 1.5
|
111
|
+
include Docker::Compose::Future::Session
|
353
112
|
end
|
354
113
|
end
|