centurion 1.4.2 → 1.5.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 +41 -5
- data/bin/centurion +19 -10
- data/lib/centurion/deploy.rb +29 -3
- data/lib/centurion/deploy_dsl.rb +8 -0
- data/lib/centurion/docker_server.rb +12 -4
- data/lib/centurion/version.rb +1 -1
- data/lib/tasks/deploy.rake +6 -2
- data/spec/deploy_spec.rb +40 -7
- data/spec/docker_server_spec.rb +35 -1
- data/spec/support/matchers/exit_code_matches.rb +38 -0
- metadata +4 -2
data/README.md
CHANGED
@@ -25,7 +25,7 @@ Commercial Docker Registry Providers:
|
|
25
25
|
Open-source:
|
26
26
|
- The [Docker registry](https://github.com/dotcloud/docker-registry) project,
|
27
27
|
built and maintained by Docker. You host this yourself.
|
28
|
-
- (*NEW!*) [Dogestry](https://github.com/
|
28
|
+
- (*NEW!*) [Dogestry](https://github.com/dogestry/dogestry) is an
|
29
29
|
s3-backed Docker registry alternative that removes the requirement to set up
|
30
30
|
a centralized registry service or host anything yourself.
|
31
31
|
|
@@ -184,6 +184,23 @@ This is currently pretty inflexible in that the hostname will now be the same
|
|
184
184
|
on all of your hosts. A possible future addition is the ability to pass
|
185
185
|
a block to be evaluated on each host.
|
186
186
|
|
187
|
+
### CGroup Resource Constraints
|
188
|
+
|
189
|
+
Limits on memory and CPU can be specified with the `memory` and `cpu_shares`
|
190
|
+
settings. Both of these expect a 64-bit integer describing the number of
|
191
|
+
bytes, and the number of CPU shares, respectively.
|
192
|
+
|
193
|
+
For example, to limit the memory to 1G, and the cpu time slice to half the
|
194
|
+
normal length, include the following:
|
195
|
+
|
196
|
+
```ruby
|
197
|
+
memory 1024000000
|
198
|
+
cpu_shares 512
|
199
|
+
```
|
200
|
+
|
201
|
+
For more information on Docker's CGroup limits see [the Docker
|
202
|
+
docs](https://docs.docker.com/reference/run/#runtime-constraints-on-cpu-and-memory).
|
203
|
+
|
187
204
|
### Interpolation
|
188
205
|
|
189
206
|
Currently there is one special string for interpolation that can be added to
|
@@ -317,7 +334,7 @@ $ bundle exec centurion -p radio-radio -e staging -a list
|
|
317
334
|
|
318
335
|
Centurion needs to have access to some registry in order to pull images to
|
319
336
|
remote Docker servers. This needs to be either a hosted registry (public or
|
320
|
-
private), or [Dogestry](https://github.com/
|
337
|
+
private), or [Dogestry](https://github.com/dogestry/dogestry).
|
321
338
|
|
322
339
|
#### Access to the registry
|
323
340
|
|
@@ -340,7 +357,7 @@ These correspond to the following settings:
|
|
340
357
|
Centurion normally uses the built-in registry support in the Docker daemon to
|
341
358
|
handle pushing and pulling images. But Centurion also has the ability to use
|
342
359
|
external tooling to support hosting your registry on Amazon S3. That tooling is
|
343
|
-
from a project called [Dogestry](https://github.com/
|
360
|
+
from a project called [Dogestry](https://github.com/dogestry/dogestry).
|
344
361
|
We have recently improved that tooling substantially in coordination with the
|
345
362
|
Centurion support.
|
346
363
|
|
@@ -349,7 +366,7 @@ with Amazon S3 to provide reliable hosting of images. Setting Centurion up to
|
|
349
366
|
use Dogestry is pretty trivial:
|
350
367
|
|
351
368
|
1. Install Dogestry binaries on the client from which Dogestry is run.
|
352
|
-
Binaries are provided in the [GitHub release](https://github.com/
|
369
|
+
Binaries are provided in the [GitHub release](https://github.com/dogestry/dogestry).
|
353
370
|
1. Add the settings necessary to get Centurion to pull from Dogestry. A config
|
354
371
|
example is provided below:
|
355
372
|
|
@@ -365,6 +382,24 @@ namespace :environment do
|
|
365
382
|
end
|
366
383
|
```
|
367
384
|
|
385
|
+
Development
|
386
|
+
-----------
|
387
|
+
|
388
|
+
Sometimes when you're doing development you want to try out some configuration
|
389
|
+
settings in environment variables that aren't in the config yet. Or perhaps you
|
390
|
+
want to override existing settings to test with. You can provide the
|
391
|
+
`--override-env` command line flag with some overrides or new variables to set.
|
392
|
+
Here's how to use it:
|
393
|
+
|
394
|
+
```bash
|
395
|
+
$ centurion -e development -a deploy -p radio-radio --override-env=SERVICE_PORT=8080,NAME=radio
|
396
|
+
```
|
397
|
+
|
398
|
+
Centurion is aimed at repeatable deployments so we don't recommend that you use
|
399
|
+
this functionality for production deployments. It will work, but it means that
|
400
|
+
the config is not the whole source of truth for your container configuration.
|
401
|
+
Caveat emptor.
|
402
|
+
|
368
403
|
Future Additions
|
369
404
|
----------------
|
370
405
|
|
@@ -388,6 +423,7 @@ Pull requests should:
|
|
388
423
|
* Have a description that explains the need for the changes
|
389
424
|
* Include tests!
|
390
425
|
* Not break the public API
|
426
|
+
* Add yourself to the CONTRIBUTORS file. I might forget.
|
391
427
|
|
392
428
|
If you are simply looking to contribute to the project, taking on one of the
|
393
429
|
items in the "Future Additions" section above would be a great place to start.
|
@@ -400,4 +436,4 @@ patents, and ideas in that code in our products if we so choose. You also agree
|
|
400
436
|
the code is provided as-is and you provide no warranties as to its fitness or
|
401
437
|
correctness for any purpose
|
402
438
|
|
403
|
-
Copyright (c) 2014 New Relic, Inc. All rights reserved.
|
439
|
+
Copyright (c) 2014-2015 New Relic, Inc. All rights reserved.
|
data/bin/centurion
CHANGED
@@ -22,16 +22,17 @@ Dir.glob(File.join(task_dir, '*.rake')).each { |file| load file }
|
|
22
22
|
require 'trollop'
|
23
23
|
|
24
24
|
opts = Trollop::options do
|
25
|
-
opt :project, 'project (blog, forums...)',
|
26
|
-
opt :environment, "environment (production, staging...)",
|
27
|
-
opt :action, 'action (deploy, list...)',
|
28
|
-
opt :image, 'image (yourco/project...)',
|
29
|
-
opt :tag, 'tag (latest...)',
|
30
|
-
opt :hosts, 'hosts, comma separated',
|
31
|
-
opt :docker_path, 'path to docker executable
|
32
|
-
opt :no_pull, 'Skip the pull_image step',
|
33
|
-
opt :registry_user, 'user for registry auth
|
34
|
-
opt :registry_password,'password for registry auth
|
25
|
+
opt :project, 'project (blog, forums...)', type: String, required: true, short: '-p'
|
26
|
+
opt :environment, "environment (production, staging...)", type: String, required: true, short: '-e'
|
27
|
+
opt :action, 'action (deploy, list...)', type: String, default: 'list', short: '-a'
|
28
|
+
opt :image, 'image (yourco/project...)', type: String, short: '-i'
|
29
|
+
opt :tag, 'tag (latest...)', type: String, short: '-t'
|
30
|
+
opt :hosts, 'hosts, comma separated', type: String, short: '-h'
|
31
|
+
opt :docker_path, 'path to docker executable', type: String, default: 'docker', short: '-d'
|
32
|
+
opt :no_pull, 'Skip the pull_image step', type: :flag, default: false, short: '-n'
|
33
|
+
opt :registry_user, 'user for registry auth', type: String, short: :none
|
34
|
+
opt :registry_password,'password for registry auth', type: String, short: :none
|
35
|
+
opt :override_env, 'override environment variables, comma separated', type: String
|
35
36
|
end
|
36
37
|
|
37
38
|
set_current_environment(opts[:environment].to_sym)
|
@@ -55,6 +56,14 @@ set :image, opts[:image] if opts[:image]
|
|
55
56
|
set :tag, opts[:tag] if opts[:tag]
|
56
57
|
set :hosts, opts[:hosts].split(",") if opts[:hosts]
|
57
58
|
|
59
|
+
# Override environment variables when specified
|
60
|
+
if opts[:override_env]
|
61
|
+
opts[:override_env].split(',').each do |envvar|
|
62
|
+
key, value = envvar.split('=')
|
63
|
+
env_vars(key => value)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
58
67
|
# Default tag should be "latest"
|
59
68
|
set :tag, 'latest' unless any?(:tag)
|
60
69
|
set :docker_registry, Centurion::DockerRegistry::OFFICIAL_URL unless any?(:docker_registry)
|
data/lib/centurion/deploy.rb
CHANGED
@@ -4,6 +4,8 @@ module Centurion; end
|
|
4
4
|
|
5
5
|
module Centurion::Deploy
|
6
6
|
FAILED_CONTAINER_VALIDATION = 100
|
7
|
+
INVALID_CGROUP_CPUSHARES_VALUE = 101
|
8
|
+
INVALID_CGROUP_MEMORY_VALUE = 102
|
7
9
|
|
8
10
|
def stop_containers(target_server, port_bindings, timeout = 30)
|
9
11
|
public_port = public_port_for(port_bindings)
|
@@ -71,6 +73,17 @@ module Centurion::Deploy
|
|
71
73
|
false
|
72
74
|
end
|
73
75
|
|
76
|
+
def is_a_uint64?(value)
|
77
|
+
result = false
|
78
|
+
if !value.is_a? Integer
|
79
|
+
return result
|
80
|
+
end
|
81
|
+
if value < 0 || value > 0xFFFFFFFFFFFFFFFF
|
82
|
+
return result
|
83
|
+
end
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
|
74
87
|
def wait_for_load_balancer_check_interval
|
75
88
|
sleep(fetch(:rolling_deploy_check_interval, 5))
|
76
89
|
end
|
@@ -87,13 +100,26 @@ module Centurion::Deploy
|
|
87
100
|
end
|
88
101
|
end
|
89
102
|
|
90
|
-
def container_config_for(target_server, image_id, port_bindings=nil, env_vars=nil, volumes=nil, command=nil,
|
103
|
+
def container_config_for(target_server, image_id, port_bindings=nil, env_vars=nil, volumes=nil, command=nil, memory=nil, cpu_shares=nil)
|
104
|
+
|
105
|
+
if memory && ! is_a_uint64?(memory)
|
106
|
+
error "Invalid value for CGroup memory constraint: #{memory}, value must be a between 0 and 18446744073709551615"
|
107
|
+
exit(INVALID_CGROUP_MEMORY_VALUE)
|
108
|
+
end
|
109
|
+
|
110
|
+
if cpu_shares && ! is_a_uint64?(cpu_shares)
|
111
|
+
error "Invalid value for CGroup CPU constraint: #{cpu_shares}, value must be between 0 and 18446744073709551615"
|
112
|
+
exit(INVALID_CGROUP_CPUSHARES_VALUE)
|
113
|
+
end
|
114
|
+
|
91
115
|
container_config = {
|
92
116
|
'Image' => image_id,
|
93
117
|
'Hostname' => fetch(:container_hostname, target_server.hostname),
|
94
118
|
}
|
95
119
|
|
96
120
|
container_config.merge!('Cmd' => command) if command
|
121
|
+
container_config.merge!('Memory' => memory) if memory
|
122
|
+
container_config.merge!('CpuShares' => cpu_shares) if cpu_shares
|
97
123
|
|
98
124
|
if port_bindings
|
99
125
|
container_config['ExposedPorts'] ||= {}
|
@@ -119,8 +145,8 @@ module Centurion::Deploy
|
|
119
145
|
container_config
|
120
146
|
end
|
121
147
|
|
122
|
-
def start_new_container(target_server, image_id, port_bindings, volumes, env_vars=nil, command=nil)
|
123
|
-
container_config = container_config_for(target_server, image_id, port_bindings, env_vars, volumes, command)
|
148
|
+
def start_new_container(target_server, image_id, port_bindings, volumes, env_vars=nil, command=nil, memory=nil, cpu_shares=nil)
|
149
|
+
container_config = container_config_for(target_server, image_id, port_bindings, env_vars, volumes, command, memory, cpu_shares)
|
124
150
|
start_container_with_config(target_server, volumes, port_bindings, container_config)
|
125
151
|
end
|
126
152
|
|
data/lib/centurion/deploy_dsl.rb
CHANGED
@@ -34,10 +34,18 @@ class Centurion::DockerServer
|
|
34
34
|
|
35
35
|
def find_containers_by_public_port(public_port, type='tcp')
|
36
36
|
ps.select do |container|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
next unless container && container['Ports']
|
38
|
+
container['Ports'].find do |port|
|
39
|
+
port['PublicPort'] == public_port.to_i && port['Type'] == type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_containers_by_name(wanted_name)
|
45
|
+
ps.select do |container|
|
46
|
+
next unless container && container['Names']
|
47
|
+
container['Names'].find do |name|
|
48
|
+
name =~ /\A\/#{wanted_name}(-[a-f0-9]{7})?\Z/
|
41
49
|
end
|
42
50
|
end
|
43
51
|
end
|
data/lib/centurion/version.rb
CHANGED
data/lib/tasks/deploy.rake
CHANGED
@@ -91,7 +91,9 @@ namespace :deploy do
|
|
91
91
|
fetch(:port_bindings),
|
92
92
|
fetch(:binds),
|
93
93
|
fetch(:env_vars),
|
94
|
-
fetch(:command)
|
94
|
+
fetch(:command),
|
95
|
+
fetch(:memory),
|
96
|
+
fetch(:cpu_shares)
|
95
97
|
)
|
96
98
|
end
|
97
99
|
end
|
@@ -118,7 +120,9 @@ namespace :deploy do
|
|
118
120
|
fetch(:port_bindings),
|
119
121
|
fetch(:binds),
|
120
122
|
fetch(:env_vars),
|
121
|
-
fetch(:command)
|
123
|
+
fetch(:command),
|
124
|
+
fetch(:memory),
|
125
|
+
fetch(:cpu_shares)
|
122
126
|
)
|
123
127
|
|
124
128
|
skip_ports = Array(fetch(:rolling_deploy_skip_ports, [])).map(&:to_s)
|
data/spec/deploy_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'centurion/deploy'
|
2
3
|
require 'centurion/deploy_dsl'
|
3
4
|
require 'centurion/logging'
|
@@ -217,6 +218,36 @@ describe Centurion::Deploy do
|
|
217
218
|
expect(config['Cmd']).to eq(command)
|
218
219
|
end
|
219
220
|
end
|
221
|
+
|
222
|
+
context 'when cgroup limits are specified' do
|
223
|
+
let(:memory) { 10000000 }
|
224
|
+
let(:cpu_shares) { 1234 }
|
225
|
+
|
226
|
+
before do
|
227
|
+
allow(test_deploy).to receive(:error) # Make sure we don't have red output in tests
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'sets cgroup limits in the config' do
|
231
|
+
config = test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, cpu_shares)
|
232
|
+
|
233
|
+
expect(config).to be_a(Hash)
|
234
|
+
expect(config.keys).to match_array(%w{ Hostname Image Memory CpuShares })
|
235
|
+
expect(config['Memory']).to eq(10000000)
|
236
|
+
expect(config['CpuShares']).to eq(1234)
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'throws a fatal error value for Cgroup Memory limit is invalid' do
|
240
|
+
expect { config = test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, "I like pie", cpu_shares) }.to terminate.with_code(102)
|
241
|
+
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, -100, cpu_shares) }.to terminate.with_code(102)
|
242
|
+
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, 0xFFFFFFFFFFFFFFFFFF, cpu_shares) }.to terminate.with_code(102)
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'throws a fatal error value for Cgroup CPU limit is invalid' do
|
246
|
+
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, "I like pie") }.to terminate.with_code(101)
|
247
|
+
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, -100) }.to terminate.with_code(101)
|
248
|
+
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, 0xFFFFFFFFFFFFFFFFFF) }.to terminate.with_code(101)
|
249
|
+
end
|
250
|
+
end
|
220
251
|
end
|
221
252
|
|
222
253
|
describe '#start_container_with_config' do
|
@@ -359,7 +390,7 @@ describe Centurion::Deploy do
|
|
359
390
|
let(:command) { ['/bin/echo', 'hi'] }
|
360
391
|
|
361
392
|
it 'configures the container' do
|
362
|
-
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, nil, {}, nil).once
|
393
|
+
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, nil, {}, nil, nil, nil).once
|
363
394
|
|
364
395
|
allow(test_deploy).to receive(:start_container_with_config)
|
365
396
|
|
@@ -417,16 +448,18 @@ describe Centurion::Deploy do
|
|
417
448
|
end
|
418
449
|
|
419
450
|
describe '#launch_console' do
|
420
|
-
let(:bindings)
|
421
|
-
let(:volumes)
|
422
|
-
let(:env)
|
423
|
-
let(:command)
|
451
|
+
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
452
|
+
let(:volumes) { nil }
|
453
|
+
let(:env) { nil }
|
454
|
+
let(:command) { nil }
|
455
|
+
let(:memory) { nil }
|
456
|
+
let(:cpu_shares) { nil }
|
424
457
|
|
425
458
|
it 'configures the container' do
|
426
|
-
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, env, volumes, command).once
|
459
|
+
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, env, volumes, command, memory, cpu_shares).once
|
427
460
|
allow(test_deploy).to receive(:start_container_with_config)
|
428
461
|
|
429
|
-
test_deploy.start_new_container(server, 'image_id', bindings, volumes, env, command)
|
462
|
+
test_deploy.start_new_container(server, 'image_id', bindings, volumes, env, command, memory, cpu_shares)
|
430
463
|
end
|
431
464
|
|
432
465
|
it 'augments the container_config' do
|
data/spec/docker_server_spec.rb
CHANGED
@@ -2,9 +2,21 @@ require 'spec_helper'
|
|
2
2
|
require 'centurion/docker_server'
|
3
3
|
|
4
4
|
describe Centurion::DockerServer do
|
5
|
-
let(:host)
|
5
|
+
let(:host) { 'host1' }
|
6
6
|
let(:docker_path) { 'docker' }
|
7
7
|
let(:server) { Centurion::DockerServer.new(host, docker_path) }
|
8
|
+
let(:container) {
|
9
|
+
{
|
10
|
+
'Command' => '/bin/bash',
|
11
|
+
'Created' => 1414797234,
|
12
|
+
'Id' => '28970c706db0f69716af43527ed926acbd82581e1cef5e4e6ff152fce1b79972',
|
13
|
+
'Image' => 'centurion-test:latest',
|
14
|
+
'Names' => ['/centurion-783aac4'],
|
15
|
+
'Ports' => [{'PrivatePort'=>80, 'Type'=>'tcp', 'IP'=>'0.0.0.0', 'PublicPort'=>23235}],
|
16
|
+
'Status' => 'Up 3 days'
|
17
|
+
}
|
18
|
+
}
|
19
|
+
let(:ps) { [ container, {}, nil ] }
|
8
20
|
|
9
21
|
it 'knows its hostname' do
|
10
22
|
expect(server.hostname).to eq('host1')
|
@@ -40,4 +52,26 @@ describe Centurion::DockerServer do
|
|
40
52
|
allow(server).to receive(:ps).and_return(image_names.map {|name| { 'Image' => name } })
|
41
53
|
expect(server.current_tags_for('target')).to eq(%w[latest production])
|
42
54
|
end
|
55
|
+
|
56
|
+
context 'finding containers' do
|
57
|
+
before do
|
58
|
+
allow(server).to receive(:ps).and_return(ps)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'finds containers by port' do
|
62
|
+
expect(server.find_containers_by_public_port(23235, 'tcp')).to eq([container])
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'only returns correct matches by port' do
|
66
|
+
expect(server.find_containers_by_public_port(1234, 'tcp')).to be_empty
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'finds containers by name' do
|
70
|
+
expect(server.find_containers_by_name('centurion')).to eq([container])
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'only returns correct matches by name' do
|
74
|
+
expect(server.find_containers_by_name('fbomb')).to be_empty
|
75
|
+
end
|
76
|
+
end
|
43
77
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# https://gist.github.com/mmasashi/58bd7e2668836a387856
|
2
|
+
RSpec::Matchers.define :terminate do |code|
|
3
|
+
actual = nil
|
4
|
+
|
5
|
+
def supports_block_expectations?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
match do |block|
|
10
|
+
begin
|
11
|
+
block.call
|
12
|
+
rescue SystemExit => e
|
13
|
+
actual = e.status
|
14
|
+
end
|
15
|
+
actual and actual == status_code
|
16
|
+
end
|
17
|
+
|
18
|
+
chain :with_code do |status_code|
|
19
|
+
@status_code = status_code
|
20
|
+
end
|
21
|
+
|
22
|
+
failure_message do |block|
|
23
|
+
"expected block to call exit(#{status_code}) but exit" +
|
24
|
+
(actual.nil? ? " not called" : "(#{actual}) was called")
|
25
|
+
end
|
26
|
+
|
27
|
+
failure_message_when_negated do |block|
|
28
|
+
"expected block not to call exit(#{status_code})"
|
29
|
+
end
|
30
|
+
|
31
|
+
description do
|
32
|
+
"expect block to call exit(#{status_code})"
|
33
|
+
end
|
34
|
+
|
35
|
+
def status_code
|
36
|
+
@status_code ||= 0
|
37
|
+
end
|
38
|
+
end
|
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.5.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-02-24 00:00:00.000000000 Z
|
25
25
|
dependencies:
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: trollop
|
@@ -208,6 +208,7 @@ files:
|
|
208
208
|
- spec/logging_spec.rb
|
209
209
|
- spec/spec_helper.rb
|
210
210
|
- spec/support/matchers/capistrano_dsl_matchers.rb
|
211
|
+
- spec/support/matchers/exit_code_matches.rb
|
211
212
|
homepage: https://github.com/newrelic/centurion
|
212
213
|
licenses:
|
213
214
|
- MIT
|
@@ -249,3 +250,4 @@ test_files:
|
|
249
250
|
- spec/logging_spec.rb
|
250
251
|
- spec/spec_helper.rb
|
251
252
|
- spec/support/matchers/capistrano_dsl_matchers.rb
|
253
|
+
- spec/support/matchers/exit_code_matches.rb
|