centurion 1.5.1 → 1.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.
- 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:
|