centurion 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -156,11 +156,15 @@ IP address (the equivalent of `docker run --dns 172.17.42.1 ...`) like this:
156
156
  ### Container Names
157
157
 
158
158
  This is the name that shows up in the `docker ps` output. It's the name
159
- of the container, not the hostname inside the container.
159
+ of the container, not the hostname inside the container. By default
160
+ the container will be named using the name of the project as the base
161
+ of the name.
162
+
163
+ If you want to name your container something other than the project name,
164
+ use the `name` setting. The actual name for the created container will
165
+ have a random hex string appended, to avoid name conflicts when you repeatedly
166
+ deploy a project:
160
167
 
161
- If you want to name your container, use the `name` setting. The
162
- actual name for the created container will have a random hex string
163
- appended, to avoid name conflicts when you repeatedly deploy an image:
164
168
  ```ruby
165
169
  task :common do
166
170
  set :name, 'backend'
@@ -194,7 +198,7 @@ For example, to limit the memory to 1G, and the cpu time slice to half the
194
198
  normal length, include the following:
195
199
 
196
200
  ```ruby
197
- memory 1024000000
201
+ memory 1.gigabyte
198
202
  cpu_shares 512
199
203
  ```
200
204
 
@@ -203,29 +207,31 @@ docs](https://docs.docker.com/reference/run/#runtime-constraints-on-cpu-and-memo
203
207
 
204
208
  ### Interpolation
205
209
 
206
- Currently there is one special string for interpolation that can be added to
207
- any `env_var` value in the DSL. `%DOCKER_HOSTNAME%` will be replaced with the
208
- current server's hostname in the environment variable at deployment time.
210
+ Currently there a couple of special strings for interpolation that can be added
211
+ to any `env_var` value in the DSL. `%DOCKER_HOSTNAME%` will be replaced with
212
+ the current server's hostname in the environment variable at deployment time.
213
+ Also `%DOCKER_HOST_IP%` will be replaced with the *public* IP address of the
214
+ Docker server using a `getaddrinfo` call on the client.
209
215
 
210
216
  ### Use TLS certificate
211
217
 
212
- Centurion can use your certificate in order to execute the Docker commands.
213
- In order to do so you have 2 choices.
218
+ Centurion can use your existing Docker TLS certificates when using Docker with
219
+ TLS support. In doing so you have 2 choices.
214
220
 
215
- #### Your certificate files are in ~/.docker/
221
+ #### Your certificate files are in `~/.docker/`
216
222
 
217
223
  You just need to enable the tls mode as the following:
218
224
 
219
225
  ```ruby
220
226
  task :production => :common do
221
- set :tls, true
227
+ set :tlsverify, true
222
228
  # ...
223
229
  end
224
230
  ```
225
231
 
226
232
  Centurion will only set the `--tlsverify` to true and Docker will read your certificate from the `~/.docker/` path.
227
233
 
228
- #### Your certificate files are not in ~/.docker/
234
+ #### Your certificate files are not in `~/.docker/`
229
235
 
230
236
  Given your files are in `/usr/local/certs/`
231
237
  You have to set the following keys:
@@ -251,7 +257,7 @@ A rolling deployment will stop and start each container one at a time to make
251
257
  sure that the application stays available from the viewpoint of the load
252
258
  balancer. As the deploy runs, a health check will hit each container to ensure
253
259
  that the application booted correctly. By default, this will be a GET request to
254
- the root path of the application. This is configurable by adding
260
+ the root path of the application. The healthcheck endpoint is configurable by adding
255
261
  `set(:status_endpoint, '/somewhere/else')` in your config. The status endpoint
256
262
  must respond with a valid response in the 200 status range.
257
263
 
@@ -259,6 +265,38 @@ must respond with a valid response in the 200 status range.
259
265
  $ bundle exec centurion -p radio-radio -e staging -a rolling_deploy
260
266
  ````
261
267
 
268
+ **Custom Health Check**:
269
+ You can use a custom health check by specifying a callable object (anything that
270
+ responds to :call), e.g. a Proc, lambda, or method. This method will be invoked with
271
+ the host url, the port that needs to be checked, and the specified endpoint(via
272
+ `set(:status_endpoint, '/somewhere/else')`). If the port is ready, health check
273
+ should return a truthy value, falsey otherwise. Here's an example of a custom
274
+ health check that verifies that an elasticsearch node is up and has joined the
275
+ cluster.
276
+
277
+ ````ruby
278
+ def cluster_green?(target_server, port, endpoint)
279
+ response = begin
280
+ Excon.get("http://#{target_server.hostname}:#{port}#{endpoint}")
281
+ rescue Excon::Errors::SocketError
282
+ warn "Elasticsearch node not yet up"
283
+ nil
284
+ end
285
+
286
+ return false unless response
287
+ !JSON.parse(response)['timed_out']
288
+ end
289
+
290
+ task :production => :common do
291
+ set_current_environment(:production)
292
+ set :status_endpoint, "/_cluster/health?wait_for_status=green&wait_for_nodes=2"
293
+ health_check method(:cluster_green?)
294
+ host_port 9200, container_port: 9200
295
+ host 'es-01.example.com'
296
+ host 'es-02.example.com'
297
+ end
298
+ ````
299
+
262
300
  **Rolling Deployment Settings**:
263
301
  You can change the following settings in your config to tune how the rolling
264
302
  deployment behaves. Each of these is controlled with `set(:var_name, 'value')`.
@@ -277,16 +315,17 @@ are the same everywhere. Settings are per-project.
277
315
  an individual container to come up before giving up as a failure. Defaults
278
316
  to 24 attempts.
279
317
  * `rolling_deploy_skip_ports` => Either a single port, or an array of ports
280
- that should be skipped for status checks. Status checking assumes an HTTP
281
- server is on the other end and if you are deploying a container where some
318
+ that should be skipped for status checks. By default status checking assumes
319
+ an HTTP server is on the other end and if you are deploying a container where some
282
320
  ports are not HTTP services, this allows you to only health check the ports
283
- that are. The default is an empty array.
321
+ that are. The default is an empty array. If you have non-HTTP services that you
322
+ want to check, see Custom Health Checks in the previous section.
284
323
 
285
324
  ###Deploy a project to a fleet of Docker servers
286
325
 
287
326
  This will hard stop, then start containers on all the specified hosts. This
288
327
  is not recommended for apps where one endpoint needs to be available at all
289
- times.
328
+ times. It is fast.
290
329
 
291
330
  ````bash
292
331
  $ bundle exec centurion -p radio-radio -e staging -a deploy
@@ -340,7 +379,7 @@ private), or [Dogestry](https://github.com/dogestry/dogestry).
340
379
 
341
380
  If you are not using either Dogestry, or the public registry, you may need to
342
381
  provide authentication credentials. Centurion needs to access the Docker
343
- registry hosting your images directly to retrive image ids and tags.This is
382
+ registry hosting your images directly to retrive image ids and tags. This is
344
383
  supported in both the config file and also as command line arguments.
345
384
 
346
385
  The command line arguments are:
@@ -365,6 +404,8 @@ Dogestry uses the Docker daemon's import/export functionality in combination
365
404
  with Amazon S3 to provide reliable hosting of images. Setting Centurion up to
366
405
  use Dogestry is pretty trivial:
367
406
 
407
+ 1. Create an S3 bucket and download the credentials to let you access the
408
+ bucket. Generally these are IAM user keys.
368
409
  1. Install Dogestry binaries on the client from which Dogestry is run.
369
410
  Binaries are provided in the [GitHub release](https://github.com/dogestry/dogestry).
370
411
  1. Add the settings necessary to get Centurion to pull from Dogestry. A config
@@ -407,8 +448,6 @@ We're currently looking at the following feature additions:
407
448
 
408
449
  * [etcd](https://github.com/coreos/etcd) integration for configs and discovery
409
450
  * Add the ability to show all the available tasks on the command line
410
- * Customized tasks
411
- * Dynamic host allocation to a pool of servers
412
451
 
413
452
  Contributions
414
453
  -------------
@@ -76,4 +76,5 @@ set :no_pull, opts[:no_pull_given]
76
76
  set :registry_user, opts[:registry_user] if opts[:registry_user]
77
77
  set :registry_password, opts[:registry_password] if opts[:registry_password]
78
78
 
79
+ invoke('centurion:setup')
79
80
  invoke(opts[:action])
@@ -1,3 +1,7 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'core_ext', '*')].each do |file|
2
+ require file
3
+ end
4
+
1
5
  Dir[File.join(File.dirname(__FILE__), 'centurion', '*')].each do |file|
2
6
  require file
3
7
  end
@@ -1,4 +1,5 @@
1
1
  require 'excon'
2
+ require 'socket'
2
3
 
3
4
  module Centurion; end
4
5
 
@@ -18,10 +19,10 @@ module Centurion::Deploy
18
19
  end
19
20
  end
20
21
 
21
- def wait_for_http_status_ok(target_server, port, endpoint, image_id, tag, sleep_time=5, retries=12)
22
+ def wait_for_health_check_ok(health_check_method, target_server, port, endpoint, image_id, tag, sleep_time=5, retries=12)
22
23
  info 'Waiting for the port to come up'
23
24
  1.upto(retries) do
24
- if container_up?(target_server, port) && http_status_ok?(target_server, port, endpoint)
25
+ if container_up?(target_server, port) && health_check_method.call(target_server, port, endpoint)
25
26
  info 'Container is up!'
26
27
  break
27
28
  end
@@ -30,7 +31,7 @@ module Centurion::Deploy
30
31
  sleep(sleep_time)
31
32
  end
32
33
 
33
- unless http_status_ok?(target_server, port, endpoint)
34
+ unless health_check_method.call(target_server, port, endpoint)
34
35
  error "Failed to validate started container on #{target_server}:#{port}"
35
36
  exit(FAILED_CONTAINER_VALIDATION)
36
37
  end
@@ -130,7 +131,7 @@ module Centurion::Deploy
130
131
 
131
132
  if env_vars
132
133
  container_config['Env'] = env_vars.map do |k,v|
133
- "#{k}=#{v.gsub('%DOCKER_HOSTNAME%', target_server.hostname)}"
134
+ "#{k}=#{interpolate_var(v, target_server)}"
134
135
  end
135
136
  end
136
137
 
@@ -206,4 +207,16 @@ module Centurion::Deploy
206
207
 
207
208
  new_container
208
209
  end
210
+
211
+ def interpolate_var(val, target_server)
212
+ val.gsub('%DOCKER_HOSTNAME%', target_server.hostname)
213
+ .gsub('%DOCKER_HOST_IP%', host_ip(target_server.hostname))
214
+ end
215
+
216
+ def host_ip(hostname)
217
+ @host_ip ||= {}
218
+ return @host_ip[hostname] if @host_ip.has_key?(hostname)
219
+ @host_ip[hostname] = Socket.getaddrinfo(hostname, nil).first[2]
220
+ end
221
+
209
222
  end
@@ -80,6 +80,11 @@ module Centurion::DeployDSL
80
80
  set(:registry, type.to_s)
81
81
  end
82
82
 
83
+ def health_check(method)
84
+ abort("Health check expects a callable (lambda, proc, method), but #{method.class} was specified") unless method.respond_to?(:call)
85
+ set(:health_check, method)
86
+ end
87
+
83
88
  private
84
89
 
85
90
  def build_server_group
@@ -7,7 +7,7 @@ module Centurion; end
7
7
 
8
8
  class Centurion::DockerViaApi
9
9
  def initialize(hostname, port, tls_args = {})
10
- @tls_args = tls_args # Required by tls_enable?
10
+ @tls_args = default_tls_args(tls_args[:tls]).merge(tls_args.reject { |k, v| v.nil? }) # Required by tls_enable?
11
11
  @base_uri = "http#{'s' if tls_enable?}://#{hostname}:#{port}"
12
12
 
13
13
  configure_excon_globally
@@ -142,4 +142,16 @@ class Centurion::DockerViaApi
142
142
  Excon.defaults[:tcp_nodelay] = true
143
143
  Excon.defaults[:ssl_ca_file] = @tls_args[:tlscacert]
144
144
  end
145
+
146
+ def default_tls_args(tls_enabled)
147
+ if tls_enabled
148
+ {
149
+ tlscacert: File.expand_path('~/.docker/ca.pem'),
150
+ tlscert: File.expand_path('~/.docker/cert.pem'),
151
+ tlskey: File.expand_path('~/.docker/key.pem')
152
+ }
153
+ else
154
+ {}
155
+ end
156
+ end
145
157
  end
@@ -1,5 +1,6 @@
1
1
  require 'pty'
2
2
  require_relative 'logging'
3
+ require_relative 'shell'
3
4
 
4
5
  module Centurion; end
5
6
 
@@ -14,16 +15,16 @@ class Centurion::DockerViaCli
14
15
 
15
16
  def pull(image, tag='latest')
16
17
  info 'Using CLI to pull'
17
- echo(build_command(:pull, "#{image}:#{tag}"))
18
+ Centurion::Shell.echo(build_command(:pull, "#{image}:#{tag}"))
18
19
  end
19
20
 
20
21
  def tail(container_id)
21
22
  info "Tailing the logs on #{container_id}"
22
- echo(build_command(:logs, container_id))
23
+ Centurion::Shell.echo(build_command(:logs, container_id))
23
24
  end
24
25
 
25
26
  def attach(container_id)
26
- echo(build_command(:attach, container_id))
27
+ Centurion::Shell.echo(build_command(:attach, container_id))
27
28
  end
28
29
 
29
30
  private
@@ -37,7 +38,7 @@ class Centurion::DockerViaCli
37
38
  end
38
39
 
39
40
  def tls_parameters
40
- return '' if @tls_args.nil? || @tls_args == {}
41
+ return '' if @tls_args.nil? || @tls_args.empty?
41
42
 
42
43
  tls_flags = ''
43
44
 
@@ -1,4 +1,5 @@
1
1
  require_relative 'logging'
2
+ require_relative 'shell'
2
3
  require 'fileutils'
3
4
 
4
5
  module Centurion; end
@@ -72,7 +73,6 @@ class Centurion::Dogestry
72
73
  hosts = pull_hosts.join(",")
73
74
  flags = "-pullhosts #{hosts}"
74
75
 
75
- echo(exec_command('pull', repo, flags))
76
+ Centurion::Shell.echo(exec_command('pull', repo, flags))
76
77
  end
77
-
78
78
  end
@@ -20,49 +20,6 @@ module Centurion::Logging
20
20
  log.debug args.join(' ')
21
21
  end
22
22
 
23
- def echo(command)
24
- if Thread.list.find_all { |t| t.status == 'run' }.count > 1
25
- run_without_echo(command)
26
- else
27
- run_with_echo(command)
28
- end
29
- end
30
-
31
- def run_without_echo(command)
32
- output = Queue.new
33
- output_thread = Thread.new do
34
- while true do
35
- begin
36
- puts output.pop
37
- rescue => e
38
- info "Rescuing... #{e.message}"
39
- end
40
- end
41
- end
42
-
43
- IO.popen(command) do |io|
44
- io.each_line { |line| output << line }
45
- end
46
-
47
- output_thread.kill
48
- validate_status(command)
49
- end
50
-
51
- def run_with_echo(command)
52
- $stdout.sync = true
53
- $stderr.sync = true
54
- IO.popen(command) do |io|
55
- io.each_char { |char| print char }
56
- end
57
- validate_status(command)
58
- end
59
-
60
- def validate_status(command)
61
- unless $?.success?
62
- raise "The command failed with a non-zero exit status: #{$?.exitstatus}. Command: '#{command}'"
63
- end
64
- end
65
-
66
23
  private
67
24
 
68
25
  def log(*args)
@@ -0,0 +1,46 @@
1
+ module Centurion; end
2
+
3
+ module Centurion::Shell
4
+ def self.echo(command)
5
+ if Thread.list.find_all { |t| t.status == 'run' }.count > 1
6
+ run_without_echo(command)
7
+ else
8
+ run_with_echo(command)
9
+ end
10
+ end
11
+
12
+ def self.run_without_echo(command)
13
+ output = Queue.new
14
+ output_thread = Thread.new do
15
+ while true do
16
+ begin
17
+ puts output.pop
18
+ rescue => e
19
+ info "Rescuing... #{e.message}"
20
+ end
21
+ end
22
+ end
23
+
24
+ IO.popen(command) do |io|
25
+ io.each_line { |line| output << line }
26
+ end
27
+
28
+ output_thread.kill
29
+ validate_status(command)
30
+ end
31
+
32
+ def self.run_with_echo(command)
33
+ $stdout.sync = true
34
+ $stderr.sync = true
35
+ IO.popen(command) do |io|
36
+ io.each_char { |char| print char }
37
+ end
38
+ validate_status(command)
39
+ end
40
+
41
+ def self.validate_status(command)
42
+ unless $?.success?
43
+ raise "The command failed with a non-zero exit status: #{$?.exitstatus}. Command: '#{command}'"
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,3 @@
1
1
  module Centurion
2
- VERSION = '1.5.1'
2
+ VERSION = '1.6.0'
3
3
  end
@@ -0,0 +1,92 @@
1
+ # Copied from ActiveSupport 4.2.1 lib/active_support/core_ext/numeric/bytes.rb
2
+ #
3
+ # NOTE that THIS LICENSE ONLY APPLIES TO THIS FILE itself, not
4
+ # to the rest of the project.
5
+ #
6
+ # ORIGINAL ACTIVE SUPPORT LICENSE FOLLOWS:
7
+ #
8
+ # Copyright (c) 2005-2015 David Heinemeier Hansson
9
+ #
10
+ # Permission is hereby granted, free of charge, to any person obtaining
11
+ # a copy of this software and associated documentation files (the
12
+ # "Software"), to deal in the Software without restriction, including
13
+ # without limitation the rights to use, copy, modify, merge, publish,
14
+ # distribute, sublicense, and/or sell copies of the Software, and to
15
+ # permit persons to whom the Software is furnished to do so, subject to
16
+ # the following conditions:
17
+ #
18
+ # The above copyright notice and this permission notice shall be
19
+ # included in all copies or substantial portions of the Software.
20
+ #
21
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
+
29
+ class Numeric
30
+ KILOBYTE = 1024
31
+ MEGABYTE = KILOBYTE * 1024
32
+ GIGABYTE = MEGABYTE * 1024
33
+ TERABYTE = GIGABYTE * 1024
34
+ PETABYTE = TERABYTE * 1024
35
+ EXABYTE = PETABYTE * 1024
36
+
37
+ # Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
38
+ #
39
+ # 2.bytes # => 2
40
+ def bytes
41
+ self
42
+ end
43
+ alias :byte :bytes
44
+
45
+ # Returns the number of bytes equivalent to the kilobytes provided.
46
+ #
47
+ # 2.kilobytes # => 2048
48
+ def kilobytes
49
+ self * KILOBYTE
50
+ end
51
+ alias :kilobyte :kilobytes
52
+
53
+ # Returns the number of bytes equivalent to the megabytes provided.
54
+ #
55
+ # 2.megabytes # => 2_097_152
56
+ def megabytes
57
+ self * MEGABYTE
58
+ end
59
+ alias :megabyte :megabytes
60
+
61
+ # Returns the number of bytes equivalent to the gigabytes provided.
62
+ #
63
+ # 2.gigabytes # => 2_147_483_648
64
+ def gigabytes
65
+ self * GIGABYTE
66
+ end
67
+ alias :gigabyte :gigabytes
68
+
69
+ # Returns the number of bytes equivalent to the terabytes provided.
70
+ #
71
+ # 2.terabytes # => 2_199_023_255_552
72
+ def terabytes
73
+ self * TERABYTE
74
+ end
75
+ alias :terabyte :terabytes
76
+
77
+ # Returns the number of bytes equivalent to the petabytes provided.
78
+ #
79
+ # 2.petabytes # => 2_251_799_813_685_248
80
+ def petabytes
81
+ self * PETABYTE
82
+ end
83
+ alias :petabyte :petabytes
84
+
85
+ # Returns the number of bytes equivalent to the exabytes provided.
86
+ #
87
+ # 2.exabytes # => 2_305_843_009_213_693_952
88
+ def exabytes
89
+ self * EXABYTE
90
+ end
91
+ alias :exabyte :exabytes
92
+ end
@@ -0,0 +1,12 @@
1
+ # The centurion:setup task is always run first to do any prework needed
2
+ # to ensure a consistent and functioning environment
3
+
4
+ namespace :centurion do
5
+ task :setup => [:clean_environment]
6
+
7
+ task :clean_environment do
8
+ ENV.delete('DOCKER_HOST')
9
+ ENV.delete('DOCKER_TLS_VERIFY')
10
+ ENV.delete('DOCKER_CERT_PATH')
11
+ end
12
+ end
@@ -131,7 +131,8 @@ namespace :deploy do
131
131
  port = host_ports.first['HostPort']
132
132
  next if skip_ports.include?(port)
133
133
 
134
- wait_for_http_status_ok(
134
+ wait_for_health_check_ok(
135
+ fetch(:health_check, method(:http_status_ok?)),
135
136
  server,
136
137
  port,
137
138
  fetch(:status_endpoint, '/'),
@@ -1,14 +1,10 @@
1
1
  require 'spec_helper'
2
- require 'centurion/deploy'
3
- require 'centurion/deploy_dsl'
4
- require 'centurion/logging'
2
+ require 'centurion'
5
3
 
6
4
  describe Centurion::Deploy do
7
- let(:mock_ok_status) { double('http_status_ok').tap { |s| allow(s).to receive(:status).and_return(200) } }
8
- let(:mock_bad_status) { double('http_status_ok').tap { |s| allow(s).to receive(:status).and_return(500) } }
9
- let(:server) { double('docker_server').tap { |s|
10
- allow(s).to receive(:hostname).and_return(hostname)
11
- allow(s).to receive(:attach) } }
5
+ let(:mock_ok_status) { double('http_status_ok', status: 200) }
6
+ let(:mock_bad_status) { double('http_status_ok', status: 500) }
7
+ let(:server) { double('docker_server', attach: true, hostname: hostname) }
12
8
  let(:port) { 8484 }
13
9
  let(:container) { { 'Ports' => [{ 'PublicPort' => port }, 'Created' => Time.now.to_i ], 'Id' => '21adfd2ef2ef2349494a', 'Names' => [ 'name1' ] } }
14
10
  let(:endpoint) { '/status/check' }
@@ -24,6 +20,7 @@ describe Centurion::Deploy do
24
20
  before do
25
21
  allow(test_deploy).to receive(:fetch).and_return nil
26
22
  allow(test_deploy).to receive(:fetch).with(:container_hostname, hostname).and_return(hostname)
23
+ allow(test_deploy).to receive(:host_ip).and_return('172.16.0.1')
27
24
  end
28
25
 
29
26
  describe '#http_status_ok?' do
@@ -82,7 +79,7 @@ describe Centurion::Deploy do
82
79
  allow(test_deploy).to receive(:container_up?).and_return(true)
83
80
  allow(test_deploy).to receive(:http_status_ok?).and_return(true)
84
81
 
85
- test_deploy.wait_for_http_status_ok(server, port, '/foo', 'image_id', 'chaucer')
82
+ test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, port, '/foo', 'image_id', 'chaucer')
86
83
  expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
87
84
  expect(test_deploy).to have_received(:info).with('Container is up!')
88
85
  end
@@ -94,7 +91,7 @@ describe Centurion::Deploy do
94
91
  expect(test_deploy).to receive(:exit)
95
92
  expect(test_deploy).to receive(:sleep).with(0)
96
93
 
97
- test_deploy.wait_for_http_status_ok(server, port, '/foo', 'image_id', 'chaucer', 0, 1)
94
+ test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, port, '/foo', 'image_id', 'chaucer', 0, 1)
98
95
  expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
99
96
  end
100
97
 
@@ -105,7 +102,7 @@ describe Centurion::Deploy do
105
102
  allow(test_deploy).to receive(:warn)
106
103
  expect(test_deploy).to receive(:exit)
107
104
 
108
- test_deploy.wait_for_http_status_ok(server, port, '/foo', 'image_id', 'chaucer', 1, 0)
105
+ test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, port, '/foo', 'image_id', 'chaucer', 1, 0)
109
106
  expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
110
107
  end
111
108
  end
@@ -178,7 +175,11 @@ describe Centurion::Deploy do
178
175
  end
179
176
 
180
177
  context 'when env vars are specified' do
181
- let(:env) { { 'FOO' => 'BAR', 'BAZ' => '%DOCKER_HOSTNAME%.example.com' } }
178
+ let(:env) { {
179
+ 'FOO' => 'BAR',
180
+ 'BAZ' => '%DOCKER_HOSTNAME%.example.com',
181
+ 'BAR' => '%DOCKER_HOST_IP%:1234'
182
+ } }
182
183
 
183
184
  it 'sets the Env key in the config' do
184
185
  config = test_deploy.container_config_for(server, image_id, port_bindings, env)
@@ -193,6 +194,12 @@ describe Centurion::Deploy do
193
194
 
194
195
  expect(config['Env']).to include('BAZ=host1.example.com')
195
196
  end
197
+
198
+ it 'interpolates the host IP into the env_vars' do
199
+ config = test_deploy.container_config_for(server, image_id, port_bindings, env)
200
+
201
+ expect(config['Env']).to include('BAR=172.16.0.1:1234')
202
+ end
196
203
  end
197
204
 
198
205
  context 'when volumes are specified' do
@@ -247,6 +254,12 @@ describe Centurion::Deploy do
247
254
  expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, -100) }.to terminate.with_code(101)
248
255
  expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, 0xFFFFFFFFFFFFFFFFFF) }.to terminate.with_code(101)
249
256
  end
257
+
258
+ it 'still works when memory is specified in gigabytes' do
259
+ memory = 3.gigabytes
260
+ config = test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, cpu_shares)
261
+ expect(config['Memory']).to eq(3 * 1024 * 1024 * 1024)
262
+ end
250
263
  end
251
264
  end
252
265
 
@@ -23,7 +23,7 @@ describe Centurion::DockerServerGroup do
23
23
  end
24
24
 
25
25
  it 'can run parallel operations' do
26
- item = double('item').tap { |i| allow(i).to receive(:dummy_method) }
26
+ item = double('item', dummy_method: true)
27
27
  expect(item).to receive(:dummy_method).twice
28
28
 
29
29
  expect { group.each_in_parallel { |host| item.dummy_method } }.not_to raise_error
@@ -237,6 +237,21 @@ describe Centurion::DockerViaApi do
237
237
  end
238
238
  end
239
239
 
240
+ context 'with default TLS certificates' do
241
+ let(:excon_uri) { "https://#{hostname}:#{port}/" }
242
+ let(:tls_args) { { tls: true } }
243
+ let(:api) { Centurion::DockerViaApi.new(hostname, port, tls_args) }
244
+
245
+ it 'lists processes' do
246
+ expect(Excon).to receive(:get).
247
+ with(excon_uri + 'v1.7/containers/json',
248
+ client_cert: File.expand_path('~/.docker/cert.pem'),
249
+ client_key: File.expand_path('~/.docker/key.pem')).
250
+ and_return(double(body: json_string, status: 200))
251
+ expect(api.ps).to eq(json_value)
252
+ end
253
+ end
254
+
240
255
  def inspected_container_on_port(id, port)
241
256
  {
242
257
  "Id" => id.to_s,
@@ -7,26 +7,26 @@ describe Centurion::DockerViaCli do
7
7
  context 'without TLS certificates' do
8
8
  let(:docker_via_cli) { Centurion::DockerViaCli.new('host1', 2375, docker_path) }
9
9
  it 'pulls the latest image given its name' do
10
- expect(docker_via_cli).to receive(:echo).
10
+ expect(Centurion::Shell).to receive(:echo).
11
11
  with("docker -H=tcp://host1:2375 pull foo:latest")
12
12
  docker_via_cli.pull('foo')
13
13
  end
14
14
 
15
15
  it 'pulls an image given its name & tag' do
16
- expect(docker_via_cli).to receive(:echo).
16
+ expect(Centurion::Shell).to receive(:echo).
17
17
  with("docker -H=tcp://host1:2375 pull foo:bar")
18
18
  docker_via_cli.pull('foo', 'bar')
19
19
  end
20
20
 
21
21
  it 'tails logs on a container' do
22
22
  id = '12345abcdef'
23
- expect(docker_via_cli).to receive(:echo).
23
+ expect(Centurion::Shell).to receive(:echo).
24
24
  with("docker -H=tcp://host1:2375 logs -f #{id}")
25
25
  docker_via_cli.tail(id)
26
26
  end
27
27
 
28
28
  it 'should print all chars when one thread is running' do
29
- expect(docker_via_cli).to receive(:run_with_echo)
29
+ expect(Centurion::Shell).to receive(:run_with_echo)
30
30
 
31
31
  allow(Thread).to receive(:list) {[double(:status => 'run')]}
32
32
 
@@ -34,7 +34,7 @@ describe Centurion::DockerViaCli do
34
34
  end
35
35
 
36
36
  it 'should only print lines when multiple threads are running' do
37
- expect(docker_via_cli).to receive(:run_without_echo)
37
+ expect(Centurion::Shell).to receive(:run_without_echo)
38
38
 
39
39
  allow(Thread).to receive(:list) {[double(:status => 'run'), double(:status => 'run')]}
40
40
 
@@ -47,7 +47,7 @@ describe Centurion::DockerViaCli do
47
47
  let(:docker_via_cli) { Centurion::DockerViaCli.new('host1', 2375,
48
48
  docker_path, tls_args) }
49
49
  it 'pulls the latest image given its name' do
50
- expect(docker_via_cli).to receive(:echo).
50
+ expect(Centurion::Shell).to receive(:echo).
51
51
  with('docker -H=tcp://host1:2375 ' \
52
52
  '--tlsverify ' \
53
53
  '--tlscacert=/certs/ca.pem ' \
@@ -57,7 +57,7 @@ describe Centurion::DockerViaCli do
57
57
  end
58
58
 
59
59
  it 'pulls an image given its name & tag' do
60
- expect(docker_via_cli).to receive(:echo).
60
+ expect(Centurion::Shell).to receive(:echo).
61
61
  with('docker -H=tcp://host1:2375 ' \
62
62
  '--tlsverify ' \
63
63
  '--tlscacert=/certs/ca.pem ' \
@@ -68,7 +68,7 @@ describe Centurion::DockerViaCli do
68
68
 
69
69
  it 'tails logs on a container' do
70
70
  id = '12345abcdef'
71
- expect(docker_via_cli).to receive(:echo).
71
+ expect(Centurion::Shell).to receive(:echo).
72
72
  with('docker -H=tcp://host1:2375 ' \
73
73
  '--tlsverify ' \
74
74
  '--tlscacert=/certs/ca.pem ' \
@@ -79,7 +79,7 @@ describe Centurion::DockerViaCli do
79
79
 
80
80
  it 'attach to a container' do
81
81
  id = '12345abcdef'
82
- expect(docker_via_cli).to receive(:echo).
82
+ expect(Centurion::Shell).to receive(:echo).
83
83
  with('docker -H=tcp://host1:2375 ' \
84
84
  '--tlsverify ' \
85
85
  '--tlscacert=/certs/ca.pem ' \
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: centurion
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -21,7 +21,7 @@ authors:
21
21
  autorequire:
22
22
  bindir: bin
23
23
  cert_chain: []
24
- date: 2015-02-25 00:00:00.000000000 Z
24
+ date: 2015-04-14 00:00:00.000000000 Z
25
25
  dependencies:
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: trollop
@@ -192,7 +192,10 @@ files:
192
192
  - lib/centurion/docker_via_cli.rb
193
193
  - lib/centurion/dogestry.rb
194
194
  - lib/centurion/logging.rb
195
+ - lib/centurion/shell.rb
195
196
  - lib/centurion/version.rb
197
+ - lib/core_ext/numeric_bytes.rb
198
+ - lib/tasks/centurion.rake
196
199
  - lib/tasks/deploy.rake
197
200
  - lib/tasks/info.rake
198
201
  - lib/tasks/list.rake
@@ -251,3 +254,4 @@ test_files:
251
254
  - spec/spec_helper.rb
252
255
  - spec/support/matchers/capistrano_dsl_matchers.rb
253
256
  - spec/support/matchers/exit_code_matches.rb
257
+ has_rdoc: