docker-compose 0.5.1 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a85e6cd467f9c5c4a6704724d9429a4f681d065a
4
- data.tar.gz: a5f85af19b15c215b68a01e7004bf0c0cb05587e
3
+ metadata.gz: 04a52fb98e79311b5e3c771d24d41d434ab08e53
4
+ data.tar.gz: d1a04e25f5164edff5e2651ceed331fd58a20eb4
5
5
  SHA512:
6
- metadata.gz: 1a9143db8626ea989d500c29a802c79b84c50ebd66674daf9bfff474f413353a9d3203bd1860d5dce3dbefa2301fc44521db597fd56b4cdf95d9ff295d11be57
7
- data.tar.gz: f2f68bd762d659926ec4880423db394e75ef03ea72f7ee0cf7af65e06639be902f0f0d9819128f64d6dc8e4d56d06b5ceb05795ed3e800ac1383f23b3939947d
6
+ metadata.gz: 52a9df711b6120701e3359fb30c3b22bc3c87abaa7823724c39e0efb462bc288b091efbce6eb77561b42f0ab112b9ea5568ac856de18ac7511d5a08292d60c30
7
+ data.tar.gz: ecd93e2c5c33b10acde0b8bd7c15c025676a53ff43f5b5df020e07f103961ccd5833e2096e965bd0766be2f768d0abdcb55b0b0ccab27572411f8f6fa07ec7e4
@@ -0,0 +1,19 @@
1
+ 0.6
2
+ ---
3
+
4
+ #### Interface-breaking changes
5
+
6
+ - Replaced `docker:compose:server` Rake task with more general `docker:compose:host`
7
+ - Replaced `server_env` option with `host_env`
8
+ - Replaced `extra_server_env` option with `extra_host_env`
9
+ - Stopped mapping values in `extra_host_env`; they are now exported verbatim
10
+
11
+ #### New features
12
+
13
+ Produce `docker:compose:env` output that is compatible with user's login shell.
14
+
15
+ 0.5
16
+ ---
17
+
18
+ Initial public release of prototype. Features work well, but there is no test
19
+ coverage.
data/README.md CHANGED
@@ -7,7 +7,7 @@ In addition to wrapping the CLI, this gem provides an environment-variable mappi
7
7
  feature that allows you to export environment variables into your _host_ that point
8
8
  to network services exposed by containers. This allows you to run an application on
9
9
  your host for quicker and easier development, but run all of its architectural
10
- dependencies -- database, cache, adjacent microservices -- in containers. The
10
+ dependencies -- database, cache, adjacent services -- in containers. The
11
11
  dependencies can even be running on another machine, e.g. a cloud instance or a
12
12
  container cluster, provided your development machine has TCP connectivity on every
13
13
  port exposed by a container.
@@ -62,29 +62,26 @@ end
62
62
  ```
63
63
 
64
64
  Notice that `rake -T` now has a few additional tasks for invoking gem
65
- functionality. You can `docker:compose:env` to print bash export statements
66
- for host-to-container environment mapping; you can `docker:compose:up` or
67
- `docker:compose:stop` to start and stop containers.
65
+ functionality. You can `docker:compose:env` print shell exports for
66
+ host-to-container environment mapping, or you can `docker:compose:host[foo]`.
68
67
 
69
- The `docker-compose` command is a perfectly valid way to start
70
- and stop containers, but the gem provides some env-substitution functionality
71
- for your YML files that will be built into docker-compose 1.5 but is not
72
- released yet. If your YML contains `${ENV}` references, i.e. in order to
73
- point your containers at network services running on the host, then you must
74
- invoke docker-compose through Rake in order to peform the substitution.
68
+ ### Hosting a Command
75
69
 
76
- ### Mapping container IPs and ports
70
+ To run a process on your host and allow it to talk to containers, use
71
+ the `docker:compose:host` task. For example, I could enter a shell
72
+ with `rake docker:compose:host[bash]`.
73
+
74
+ Before "hosting" your command, the Rake task export ssome environment
75
+ variables that your command can use to discover services running in
76
+ containers. Your Rakefile specifies which variables your app needs
77
+ (the `host_env` option) and which container information each variable should
78
+ map to.
77
79
 
78
- Assuming that your app accepts its configuration in the form of environment
79
- variables, you can use the `docker:compose:env` to export environment values
80
- into your bash shell that point to services running inside containers. This
81
- allows you to run the app on your host (for easier debugging and code editing)
82
- but let it communicate with services running inside containers.
80
+ By hosting commands, you benefit from easier debugging and code editing of
81
+ the app you're working on, but still get to rely on containers to provide
82
+ the companion services your app requires to run.
83
83
 
84
- Docker::Compose uses a heuristic to figure out which IP your services
85
- are actually reachable at; the heuristic works regardless whether you are
86
- running "bare" docker daemon on localhost, communicating with a docker-machine
87
- instance, or even using a cloud-hosted docker machine!
84
+ ### Mapping container IPs and ports
88
85
 
89
86
  As a trivial example, let's say that your `docker-compose.yml` contains one
90
87
  service, the database that your app needs in order to run.
@@ -101,30 +98,27 @@ db:
101
98
  ```
102
99
 
103
100
  Your app needs two inputs, `DATABASE_HOST` and `DATABASE_PORT`. You can specify
104
- this in the env section of the Rake task:
101
+ this with the host_env option of the Rake task:
105
102
 
106
103
  ```ruby
107
104
  Docker::Compose::RakeTasks.new do |tasks|
108
- tasks.env = {
109
- 'DATABASE_HOST' => 'db:[3306]'
110
- 'DATABASE_PORT' => '[db]:3306'
105
+ tasks.host_env = {
106
+ 'DATABASE_HOST' => 'db:[3306]',
107
+ 'DATABASE_PORT' => '[db]:3306',
111
108
  }
112
109
  end
113
110
  ```
114
111
 
115
- (If I had a `DATABASE_URL` input, I could provide a URL such as
116
- `mysql://db/myapp_development`; Docker::Compose would parse the URL and replace
117
- the hostname and port appropriately.)
118
-
119
112
  Now, I can run my services, ask Docker::Compose to map the environment values
120
113
  to the actual IP and port that `db` has been published to, and run my app:
121
114
 
122
115
  ```bash
116
+ # First, bring up the containers we will be interested in
123
117
  user@machine$ docker-compose up -d
124
118
 
125
- # This prints bash code resembling the following:
126
- # export DATABASE_HOST=127.0.0.1
127
- # export DATABASE_PORT=34387
119
+ # The rake task prints bash code resembling the following:
120
+ # export DATABASE_HOST='127.0.0.1'
121
+ # export DATABASE_PORT='34387'
128
122
  # We eval it, which makes the variables available to our shell and to all
129
123
  # subprocesses.
130
124
  user@machine$ eval "$(bundle exec rake docker:compose:env)"
@@ -132,6 +126,24 @@ user@machine$ eval "$(bundle exec rake docker:compose:env)"
132
126
  user@machine$ bundle exec rackup
133
127
  ```
134
128
 
129
+ The `host_env` option also handles substitution of URLs, and arrays of values
130
+ (which are serialized back to the environment as JSON)
131
+ For example:
132
+
133
+ ```ruby
134
+ tasks.host_env = {
135
+ 'DATABASE_URL' => 'mysql://db:3306/myapp_development',
136
+ 'MIXED_FRUIT' => ['db:[3306]', '[db]:3306']
137
+ }
138
+ ```
139
+
140
+ This would result in the following exports:
141
+
142
+ ```bash
143
+ export DATABASE_URL='mysql://127.0.0.1:34387/myapp_development'
144
+ export MIXED_FRUIT='["127.0.0.1", "34387"]'
145
+ ```
146
+
135
147
  To learn more about mapping, read the class documentation for
136
148
  `Docker::Compose::Mapper`.
137
149
 
@@ -15,11 +15,10 @@ module Docker::Compose
15
15
  # Instantiate a mapper; map some environment variables; yield to caller for
16
16
  # additional processing.
17
17
  #
18
- # @param [Boolean] strict
19
18
  # @param [Session] session
20
19
  # @param [NetInfo] net_info
21
20
  # @yield yields with each substituted (key, value) pair
22
- def self.map(env, strict:true, session:Session.new, net_info:NetInfo.new)
21
+ def self.map(env, session:Session.new, net_info:NetInfo.new)
23
22
  # TODO: encapsulate this trickiness better ... inside NetInfo perhaps?
24
23
  docker_host = ENV['DOCKER_HOST']
25
24
  if docker_host.nil? || docker_host =~ /^(\/|unix|file)/
@@ -32,7 +31,7 @@ module Docker::Compose
32
31
  override_host = net_info.docker_routable_ip
33
32
  end
34
33
 
35
- mapper = self.new(session, override_host, strict:strict)
34
+ mapper = self.new(session, override_host)
36
35
  env.each_pair do |k, v|
37
36
  begin
38
37
  v = mapper.map(v)
@@ -47,13 +46,9 @@ module Docker::Compose
47
46
  # @param [Docker::Compose::Session] session
48
47
  # @param [String] override_host forcible address or DNS hostname to use;
49
48
  # leave nil to trust docker-compose output.
50
- # @param [Boolean] strict if true, raise BadSubstitution when unrecognized
51
- # syntax is passed to #map; if false, simply return unrecognized
52
- # values without substituting anything
53
- def initialize(session, override_host=nil, strict:true)
49
+ def initialize(session, override_host=nil)
54
50
  @session = session
55
51
  @override_host = override_host
56
- @strict = strict
57
52
  end
58
53
 
59
54
  # Substitute service hostnames and ports that appear in a URL or a
@@ -133,10 +128,8 @@ module Docker::Compose
133
128
  host, port = host_and_port(pair.first, pair.last)
134
129
  return "#{host}:#{port}"
135
130
  end
136
- elsif @strict
137
- raise BadSubstitution, "Can't understand '#{value}'"
138
131
  else
139
- return value
132
+ raise BadSubstitution, "Can't understand '#{value}'"
140
133
  end
141
134
  end
142
135
  end
@@ -5,6 +5,9 @@ require 'shellwords'
5
5
  # In case this file is required directly
6
6
  require 'docker/compose'
7
7
 
8
+ # Only used here, so only required here
9
+ require 'docker/compose/shell_printer'
10
+
8
11
  module Docker::Compose
9
12
  class RakeTasks < Rake::TaskLib
10
13
  # Set the directory in which docker-compose commands will be run. Default
@@ -17,32 +20,33 @@ module Docker::Compose
17
20
  # @return [String]
18
21
  attr_accessor :file
19
22
 
20
- # Provide a mapping of environment variables that should be set in the
21
- # _host_ shell for docker:compose:env or docker:compose:server.
23
+ # Provide a mapping of environment variables that should be set in
24
+ # _host_ processes, e.g. when running docker:compose:env or
25
+ # docker:compose:host.
26
+ #
22
27
  # The values of the environment variables can refer to names of services
23
- # and ports defined in the docker-compose file, and this gem will query
24
- # docker-compose to find out which host IP and port the services are
25
- # reachable on. This allows components running on the host to connect to
26
- # services running inside containers.
28
+ # and ports defined in the docker-compose file, and this gem will substitute
29
+ # the actual IP and port that the containers are reachable on. This allows
30
+ # commands invoked via "docker:compose:host" to reach services running
31
+ # inside containers.
27
32
  #
28
33
  # @see Docker::Compose::Mapper for information about the substitution syntax
29
- attr_accessor :server_env
30
-
31
- # Extra environment variables that should be set before invoking the command
32
- # specified for docker:compose:server. These are set _in addition_ to server_env
33
- # (and should be disjoint from server_env), and do not necessarily need to map the
34
- # location of a container; they are simply extra environment values that are
35
- # useful to change the server's behavior when it runs in cooperation
36
- # with containers.
34
+ attr_accessor :host_env
35
+
36
+ # Extra environment variables to set before invoking host processes. These
37
+ # are set _in addition_ to server_env, but are not substituted in any way
38
+ # and must not contain any service information.
37
39
  #
38
- # If there is overlap between server_env and extra_server_env, then keys
39
- # of extra_server_env will "win"; they are set last.
40
- attr_accessor :extra_server_env
40
+ # Extra host env should be disjoint from host_env; if there is overlap
41
+ # between the two, then extra_host_env will "win."
42
+ attr_accessor :extra_host_env
41
43
 
42
- # Command to exec on the _host_ when someone invokes docker:compose:server.
43
- # This is used to start up all containers and then run a server that
44
- # depends on them and is properly linked to them.
45
- attr_accessor :server
44
+ # Services to bring up with `docker-compose up` before running any hosted
45
+ # command. This is useful if your `docker-compose.yml` contains a service
46
+ # definition for the app you will be hosting; if you host the app, you
47
+ # want to specify all of the _other_ services, but not the app itself, since
48
+ # that will run on the host.
49
+ attr_accessor :host_services
46
50
 
47
51
  # Namespace to define the rake tasks under. Defaults to "docker:compose'.
48
52
  attr_accessor :rake_namespace
@@ -53,14 +57,15 @@ module Docker::Compose
53
57
  def initialize
54
58
  self.dir = Rake.application.original_dir
55
59
  self.file = 'docker-compose.yml'
56
- self.server_env = {}
57
- self.extra_server_env = {}
60
+ self.host_env = {}
61
+ self.extra_host_env = {}
58
62
  self.rake_namespace = 'docker:compose'
59
63
  yield self if block_given?
60
64
 
61
65
  @shell = Backticks::Runner.new
62
66
  @session = Docker::Compose::Session.new(@shell, dir:dir, file:file)
63
67
  @net_info = Docker::Compose::NetInfo.new
68
+ @shell_printer = Docker::Compose::ShellPrinter.new
64
69
 
65
70
  @shell.interactive = true
66
71
 
@@ -73,41 +78,22 @@ module Docker::Compose
73
78
  task :env do
74
79
  @shell.interactive = false # suppress useless 'port' output
75
80
 
76
- if Rake.application.top_level_tasks.include? 'docker:compose:env'
77
- # This task is being run as top-level task; set process
78
- # environment _and_ print bash export commands to stdout.
79
- # Also print usage hints if user invoked rake directly vs.
80
- # eval'ing it's output
81
- print_usage
82
- export_env(print:true)
83
- else
84
- # This task is a dependency of something else; just export the
85
- # environment variables for use in-process by other Rake tasks.
86
- export_env(print:false)
87
- end
81
+ print_usage if STDOUT.tty?
82
+ export_env(print:true)
88
83
 
89
84
  @shell.interactive = true
90
85
  end
91
86
 
92
- desc 'Launch services (ONLY=a,b,...)'
93
- task :up do
94
- only = (ENV['ONLY'] || '').split(',').compact.uniq
95
- @session.up(*only, detached:true)
96
- end
97
-
98
- desc 'Tail logs of all running services'
99
- task :logs do
100
- @session.logs
101
- end
87
+ desc 'Run command on host, linked to services in containers'
88
+ task :host, [:command] => ['docker:compose:env'] do |task, args|
102
89
 
103
- desc 'Stop services'
104
- task :stop do
105
- @session.stop
106
- end
90
+ if self.host_services
91
+ @session.up(*self.host_services, detached:true)
92
+ else
93
+ @session.up(detached:true)
94
+ end
107
95
 
108
- desc 'Run application on the host, linked to services in containers'
109
- task :server => ['docker:compose:up', 'docker:compose:env'] do
110
- exec(self.server)
96
+ exec(args[:command])
111
97
  end
112
98
  end
113
99
  end
@@ -116,17 +102,14 @@ module Docker::Compose
116
102
  # published by docker-compose services. Optionally also print bash export
117
103
  # statements so this information can be made available to a user's shell.
118
104
  private def export_env(print:)
119
- Docker::Compose::Mapper.map(self.server_env,
105
+ Docker::Compose::Mapper.map(self.host_env,
120
106
  session:@session,
121
107
  net_info:@net_info) do |k, v|
122
108
  ENV[k] = serialize_for_env(v)
123
109
  print_env(k, ENV[k]) if print
124
110
  end
125
111
 
126
- Docker::Compose::Mapper.map(self.extra_server_env,
127
- strict:false,
128
- session:@session,
129
- net_info:@net_info) do |k, v|
112
+ self.extra_host_env.each do |k, v|
130
113
  ENV[k] = serialize_for_env(v)
131
114
  print_env(k, ENV[k]) if print
132
115
  end
@@ -148,19 +131,21 @@ module Docker::Compose
148
131
  end
149
132
  end
150
133
 
151
- # Print a bash export or unset statement
134
+ # Print an export or unset statement suitable for user's shell
152
135
  private def print_env(k, v)
153
136
  if v
154
- puts format('export %s=%s', k, Shellwords.escape(v))
137
+ puts @shell_printer.export(k, v)
155
138
  else
156
- puts format('unset %s # service not running', k)
139
+ puts @shell_printer.unset(k)
157
140
  end
158
141
  end
159
142
 
143
+
160
144
  private def print_usage
161
- be = 'bundle exec ' if defined?(Bundler)
162
- puts %Q{# To export these variables to your shell, run:}
163
- puts %Q{# eval "$(#{be}rake docker:compose:env)"}
145
+ command = "rake #{rake_namespace}:env"
146
+ command = "bundle exec " + command if defined?(Bundler)
147
+ puts @shell_printer.comment('To export these variables to your shell, run:')
148
+ puts @shell_printer.comment(@shell_printer.eval_output(command))
164
149
  end
165
150
  end
166
151
  end
@@ -0,0 +1,21 @@
1
+ require 'etc'
2
+
3
+ module Docker::Compose
4
+ module ShellPrinter
5
+ def self.new
6
+ shell = Etc.getpwuid(Process.uid).shell
7
+ basename = File.basename(shell)
8
+
9
+ # Crappy titleize (bash -> Bash)
10
+ basename[0] = basename[0].upcase
11
+
12
+ # Find adapter class named after shell; default to posix if nothing found
13
+ klass = const_get(basename.to_sym) rescue Posix
14
+
15
+ klass.new
16
+ end
17
+ end
18
+ end
19
+
20
+ require_relative 'shell_printer/posix'
21
+ require_relative 'shell_printer/fish'
@@ -0,0 +1,16 @@
1
+ module Docker::Compose::ShellPrinter
2
+ # Printer that works with the Friendly Interactive Shell (fish).
3
+ class Fish < Posix
4
+ def eval_output(command)
5
+ format('eval (%s)', command)
6
+ end
7
+
8
+ def export(name, value)
9
+ format('set -gx %s %s; ', name, single_quoted_escaped(value))
10
+ end
11
+
12
+ def unset(name)
13
+ format('set -ex %s; ', name)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ module Docker::Compose::ShellPrinter
2
+ # Printer that works with any POSIX-compliant shell e.g. sh, bash, zsh
3
+ class Posix
4
+ def comment(value)
5
+ format('# %s', value)
6
+ end
7
+
8
+ def eval_output(command)
9
+ format('eval "$(%s)"', command)
10
+ end
11
+
12
+ def export(name, value)
13
+ format('export %s=%s', name, single_quoted_escaped(value))
14
+ end
15
+
16
+ def unset(name)
17
+ format('unset %s', name)
18
+ end
19
+
20
+ protected def single_quoted_escaped(value)
21
+ # "escape" any occurrences of ' in value by closing the single-quoted
22
+ # string, concatenating a single backslash-escaped ' character, and reopening
23
+ # the single-quoted string.
24
+ #
25
+ # This relies on the shell's willingness to concatenate adjacent string
26
+ # literals. Tested under sh, bash and zsh; should work elsewhere.
27
+ escaped = value.gsub("'") { "'\\''" }
28
+
29
+ "'#{escaped}'"
30
+ end
31
+ end
32
+ end
@@ -1,5 +1,5 @@
1
1
  module Docker
2
2
  module Compose
3
- VERSION = "0.5.1"
3
+ VERSION = "0.6.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker-compose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Spataro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-04-13 00:00:00.000000000 Z
11
+ date: 2016-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backticks
@@ -78,6 +78,7 @@ files:
78
78
  - ".rspec"
79
79
  - ".ruby-version"
80
80
  - ".travis.yml"
81
+ - CHANGELOG.md
81
82
  - CODE_OF_CONDUCT.md
82
83
  - Gemfile
83
84
  - LICENSE.txt
@@ -92,6 +93,9 @@ files:
92
93
  - lib/docker/compose/net_info.rb
93
94
  - lib/docker/compose/rake_tasks.rb
94
95
  - lib/docker/compose/session.rb
96
+ - lib/docker/compose/shell_printer.rb
97
+ - lib/docker/compose/shell_printer/fish.rb
98
+ - lib/docker/compose/shell_printer/posix.rb
95
99
  - lib/docker/compose/version.rb
96
100
  homepage: https://github.com/xeger/docker-compose
97
101
  licenses: