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 +60 -21
- data/bin/centurion +1 -0
- data/lib/centurion.rb +4 -0
- data/lib/centurion/deploy.rb +17 -4
- data/lib/centurion/deploy_dsl.rb +5 -0
- data/lib/centurion/docker_via_api.rb +13 -1
- data/lib/centurion/docker_via_cli.rb +5 -4
- data/lib/centurion/dogestry.rb +2 -2
- data/lib/centurion/logging.rb +0 -43
- data/lib/centurion/shell.rb +46 -0
- data/lib/centurion/version.rb +1 -1
- data/lib/core_ext/numeric_bytes.rb +92 -0
- data/lib/tasks/centurion.rake +12 -0
- data/lib/tasks/deploy.rake +2 -1
- data/spec/deploy_spec.rb +25 -12
- data/spec/docker_server_group_spec.rb +1 -1
- data/spec/docker_via_api_spec.rb +15 -0
- data/spec/docker_via_cli_spec.rb +9 -9
- metadata +6 -2
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
|
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
|
207
|
-
any `env_var` value in the DSL. `%DOCKER_HOSTNAME%` will be replaced with
|
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
|
213
|
-
|
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
|
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 :
|
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
|
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.
|
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.
|
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
|
-------------
|
data/bin/centurion
CHANGED
data/lib/centurion.rb
CHANGED
data/lib/centurion/deploy.rb
CHANGED
@@ -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
|
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) &&
|
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
|
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
|
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
|
data/lib/centurion/deploy_dsl.rb
CHANGED
@@ -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
|
|
data/lib/centurion/dogestry.rb
CHANGED
@@ -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
|
data/lib/centurion/logging.rb
CHANGED
@@ -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
|
data/lib/centurion/version.rb
CHANGED
@@ -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
|
data/lib/tasks/deploy.rake
CHANGED
@@ -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
|
-
|
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, '/'),
|
data/spec/deploy_spec.rb
CHANGED
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'centurion
|
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'
|
8
|
-
let(:mock_bad_status) { double('http_status_ok'
|
9
|
-
let(:server) { double('docker_server')
|
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.
|
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.
|
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.
|
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) { {
|
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'
|
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
|
data/spec/docker_via_api_spec.rb
CHANGED
@@ -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,
|
data/spec/docker_via_cli_spec.rb
CHANGED
@@ -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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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.
|
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-
|
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:
|