centurion 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CONTRIBUTORS.md +2 -0
- data/README.md +100 -15
- data/lib/centurion/deploy.rb +36 -134
- data/lib/centurion/deploy_dsl.rb +62 -21
- data/lib/centurion/docker_server.rb +12 -2
- data/lib/centurion/docker_via_api.rb +18 -10
- data/lib/centurion/dogestry.rb +16 -2
- data/lib/centurion/service.rb +218 -0
- data/lib/centurion/version.rb +1 -1
- data/lib/core_ext/numeric_bytes.rb +59 -57
- data/lib/tasks/centurion.rake +3 -0
- data/lib/tasks/deploy.rake +29 -42
- data/spec/deploy_dsl_spec.rb +78 -17
- data/spec/deploy_spec.rb +45 -343
- data/spec/docker_server_spec.rb +16 -1
- data/spec/docker_via_api_spec.rb +32 -55
- data/spec/service_spec.rb +288 -0
- metadata +27 -42
data/lib/centurion/version.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# Modified from ActiveSupport 4.2.1 lib/active_support/core_ext/numeric/bytes.rb
|
2
2
|
#
|
3
3
|
# NOTE that THIS LICENSE ONLY APPLIES TO THIS FILE itself, not
|
4
4
|
# to the rest of the project.
|
@@ -25,68 +25,70 @@
|
|
25
25
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
26
26
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
27
27
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
28
|
+
unless Numeric.constants.include?(:KILOBYTE)
|
29
|
+
class Numeric
|
30
|
+
KILOBYTE = 1024 unless defined? KILOBYTE
|
31
|
+
MEGABYTE = KILOBYTE * 1024 unless defined? MEGABYTE
|
32
|
+
GIGABYTE = MEGABYTE * 1024 unless defined? GIGABYTE
|
33
|
+
TERABYTE = GIGABYTE * 1024 unless defined? TERABYTE
|
34
|
+
PETABYTE = TERABYTE * 1024 unless defined? PETABYTE
|
35
|
+
EXABYTE = PETABYTE * 1024 unless defined? EXABYTE
|
28
36
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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 unless method_defined? :bytes
|
43
|
+
alias :byte :bytes unless method_defined? :byte
|
36
44
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
45
|
+
# Returns the number of bytes equivalent to the kilobytes provided.
|
46
|
+
#
|
47
|
+
# 2.kilobytes # => 2048
|
48
|
+
def kilobytes
|
49
|
+
self * KILOBYTE
|
50
|
+
end unless method_defined? :kilobytes
|
51
|
+
alias :kilobyte :kilobytes unless method_defined? :kilobyte
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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 unless method_defined? :megabytes?
|
59
|
+
alias :megabyte :megabytes unless method_defined? :megabyte
|
60
60
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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 unless method_defined? :gigabytes
|
67
|
+
alias :gigabyte :gigabytes unless method_defined? :gigabyte
|
68
68
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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 unless method_defined? :terabytes
|
75
|
+
alias :terabyte :terabytes unless method_defined? :terabyte
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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 unless method_defined? :petabytes
|
83
|
+
alias :petabyte :petabytes unless method_defined? :petabyte
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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 unless method_defined? :exabytes
|
91
|
+
alias :exabyte :exabytes unless method_defined? :exabyte
|
90
92
|
end
|
91
|
-
alias :exabyte :exabytes
|
92
93
|
end
|
94
|
+
|
data/lib/tasks/centurion.rake
CHANGED
@@ -7,6 +7,9 @@ namespace :centurion do
|
|
7
7
|
task :clean_environment do
|
8
8
|
ENV.delete('DOCKER_HOST')
|
9
9
|
ENV.delete('DOCKER_TLS_VERIFY')
|
10
|
+
# Preserve original DOCKER_CERT_PATH for use by dogestry.
|
11
|
+
# See also Centurion::Dogestry#set_envs
|
12
|
+
set(:original_docker_cert_path, ENV['DOCKER_CERT_PATH'])
|
10
13
|
ENV.delete('DOCKER_CERT_PATH')
|
11
14
|
end
|
12
15
|
end
|
data/lib/tasks/deploy.rake
CHANGED
@@ -25,6 +25,18 @@ end
|
|
25
25
|
|
26
26
|
task :stop => ['deploy:stop']
|
27
27
|
|
28
|
+
namespace :dev do
|
29
|
+
task :export_only do
|
30
|
+
# This removes the known-to-be-problematic bundler env
|
31
|
+
# vars but doesn't try to sanitize the whole ENV. Doing
|
32
|
+
# so breaks things like rbenv which we need to use when
|
33
|
+
# testing. A /bin/bash -l will help further clean it up.
|
34
|
+
ENV.reject! { |(var, val)| var =~ /^BUNDLE_/ }
|
35
|
+
fetch(:env_vars).each { |(var, value)| ENV[var] = value }
|
36
|
+
exec fetch(:development_shell, '/bin/bash -l')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
28
40
|
namespace :deploy do
|
29
41
|
include Centurion::Deploy
|
30
42
|
|
@@ -47,6 +59,10 @@ namespace :deploy do
|
|
47
59
|
aws_secret_key: fetch(:aws_secret_key),
|
48
60
|
s3_bucket: fetch(:s3_bucket),
|
49
61
|
s3_region: fetch(:s3_region) || 'us-east-1',
|
62
|
+
tlsverify: fetch(:tlsverify),
|
63
|
+
tlscacert: fetch(:tlscacert),
|
64
|
+
tlscert: fetch(:tlscert),
|
65
|
+
original_docker_cert_path: fetch(:original_docker_cert_path)
|
50
66
|
)
|
51
67
|
|
52
68
|
target_servers = Centurion::DockerServerGroup.new(fetch(:hosts), fetch(:docker_path))
|
@@ -75,7 +91,7 @@ namespace :deploy do
|
|
75
91
|
# - remote: stop
|
76
92
|
task :stop do
|
77
93
|
on_each_docker_host do |server|
|
78
|
-
stop_containers(server,
|
94
|
+
stop_containers(server, defined_service, fetch(:stop_timeout, 30))
|
79
95
|
end
|
80
96
|
end
|
81
97
|
|
@@ -85,59 +101,32 @@ namespace :deploy do
|
|
85
101
|
# - remote: inspect container
|
86
102
|
task :start_new do
|
87
103
|
on_each_docker_host do |server|
|
88
|
-
start_new_container(
|
89
|
-
server,
|
90
|
-
fetch(:image_id),
|
91
|
-
fetch(:port_bindings),
|
92
|
-
fetch(:binds),
|
93
|
-
fetch(:env_vars),
|
94
|
-
fetch(:command),
|
95
|
-
fetch(:memory),
|
96
|
-
fetch(:cpu_shares)
|
97
|
-
)
|
104
|
+
start_new_container(server, defined_service, defined_restart_policy)
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
101
108
|
task :launch_console do
|
102
109
|
on_each_docker_host do |server|
|
103
|
-
launch_console(
|
104
|
-
server,
|
105
|
-
fetch(:image_id),
|
106
|
-
fetch(:port_bindings),
|
107
|
-
fetch(:binds),
|
108
|
-
fetch(:env_vars)
|
109
|
-
)
|
110
|
+
launch_console(server, defined_service)
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
113
114
|
task :rolling_deploy do
|
114
115
|
on_each_docker_host do |server|
|
115
|
-
|
116
|
-
|
117
|
-
start_new_container(
|
118
|
-
server,
|
119
|
-
fetch(:image_id),
|
120
|
-
fetch(:port_bindings),
|
121
|
-
fetch(:binds),
|
122
|
-
fetch(:env_vars),
|
123
|
-
fetch(:command),
|
124
|
-
fetch(:memory),
|
125
|
-
fetch(:cpu_shares)
|
126
|
-
)
|
116
|
+
service = defined_service
|
127
117
|
|
128
|
-
|
118
|
+
stop_containers(server, service, fetch(:stop_timeout, 30))
|
129
119
|
|
130
|
-
|
131
|
-
port = host_ports.first['HostPort']
|
132
|
-
next if skip_ports.include?(port)
|
120
|
+
container = start_new_container(server, service, defined_restart_policy)
|
133
121
|
|
122
|
+
public_ports = service.public_ports - fetch(:rolling_deploy_skip_ports, [])
|
123
|
+
public_ports.each do |port|
|
134
124
|
wait_for_health_check_ok(
|
135
125
|
fetch(:health_check, method(:http_status_ok?)),
|
136
126
|
server,
|
127
|
+
container['Id'],
|
137
128
|
port,
|
138
129
|
fetch(:status_endpoint, '/'),
|
139
|
-
fetch(:image),
|
140
|
-
fetch(:tag),
|
141
130
|
fetch(:rolling_deploy_wait_time, 5),
|
142
131
|
fetch(:rolling_deploy_retries, 24)
|
143
132
|
)
|
@@ -148,8 +137,8 @@ namespace :deploy do
|
|
148
137
|
end
|
149
138
|
|
150
139
|
task :cleanup do
|
151
|
-
on_each_docker_host do |
|
152
|
-
cleanup_containers(
|
140
|
+
on_each_docker_host do |server|
|
141
|
+
cleanup_containers(server, defined_service)
|
153
142
|
end
|
154
143
|
end
|
155
144
|
|
@@ -189,10 +178,7 @@ namespace :deploy do
|
|
189
178
|
if fetch(:registry) == 'dogestry'
|
190
179
|
invoke 'deploy:dogestry:pull_image'
|
191
180
|
else
|
192
|
-
|
193
|
-
target_servers = Centurion::DockerServerGroup.new(hosts, docker_path,
|
194
|
-
build_tls_params)
|
195
|
-
target_servers.each_in_parallel do |target_server|
|
181
|
+
build_server_group.each_in_parallel do |target_server|
|
196
182
|
target_server.pull(fetch(:image), fetch(:tag))
|
197
183
|
end
|
198
184
|
end
|
@@ -250,4 +236,5 @@ namespace :deploy do
|
|
250
236
|
|
251
237
|
invoke 'deploy'
|
252
238
|
end
|
239
|
+
|
253
240
|
end
|
data/spec/deploy_dsl_spec.rb
CHANGED
@@ -23,23 +23,53 @@ describe Centurion::DeployDSL do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it 'has a DSL method for specifying the start command' do
|
26
|
-
command =
|
26
|
+
command = %w{ /bin/echo hi }
|
27
27
|
DeployDSLTest.command command
|
28
|
-
expect(DeployDSLTest.
|
28
|
+
expect(DeployDSLTest.defined_service.command).to eq(command)
|
29
29
|
end
|
30
30
|
|
31
31
|
it 'adds new env_vars to the existing ones, as strings' do
|
32
|
-
DeployDSLTest.
|
32
|
+
DeployDSLTest.env_vars('SHAKESPEARE' => 'Hamlet')
|
33
33
|
DeployDSLTest.env_vars('DICKENS' => 'David Copperfield',
|
34
34
|
:DICKENS_BIRTH_YEAR => 1812)
|
35
35
|
|
36
|
-
expect(DeployDSLTest.
|
36
|
+
expect(DeployDSLTest.defined_service.env_vars).to eq(
|
37
37
|
'SHAKESPEARE' => 'Hamlet',
|
38
38
|
'DICKENS' => 'David Copperfield',
|
39
39
|
'DICKENS_BIRTH_YEAR' => '1812'
|
40
40
|
)
|
41
41
|
end
|
42
42
|
|
43
|
+
describe '#add_capability' do
|
44
|
+
it 'adds one capability' do
|
45
|
+
DeployDSLTest.add_capability 'IPC_LOCK'
|
46
|
+
expect(DeployDSLTest.defined_service.cap_adds).to eq(['IPC_LOCK'])
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'adds multiple capabilites' do
|
50
|
+
DeployDSLTest.add_capability 'IPC_LOCK'
|
51
|
+
DeployDSLTest.add_capability 'SYS_RESOURCE'
|
52
|
+
expect(DeployDSLTest.defined_service.cap_adds).to eq(['IPC_LOCK', 'SYS_RESOURCE'])
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'fails when an invalid capability is added' do
|
56
|
+
lambda{ DeployDSLTest.add_capability 'FOO_BAR' }.should raise_error SystemExit
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#drop_capability' do
|
61
|
+
it 'drops one capability' do
|
62
|
+
DeployDSLTest.drop_capability 'IPC_LOCK'
|
63
|
+
expect(DeployDSLTest.defined_service.cap_drops).to eq(['IPC_LOCK'])
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'drops multiple capabilites' do
|
67
|
+
DeployDSLTest.drop_capability 'IPC_LOCK'
|
68
|
+
DeployDSLTest.drop_capability 'SYS_RESOURCE'
|
69
|
+
expect(DeployDSLTest.defined_service.cap_drops).to eq(['IPC_LOCK', 'SYS_RESOURCE'])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
43
73
|
it 'adds hosts to the host list' do
|
44
74
|
DeployDSLTest.set(:hosts, [ 'host1' ])
|
45
75
|
DeployDSLTest.host('host2')
|
@@ -67,25 +97,16 @@ describe Centurion::DeployDSL do
|
|
67
97
|
end
|
68
98
|
|
69
99
|
it 'adds new bind ports to the list' do
|
70
|
-
|
71
|
-
DeployDSLTest.set(:port_bindings, dummy_value)
|
100
|
+
DeployDSLTest.host_port(666, container_port: 666)
|
72
101
|
DeployDSLTest.host_port(999, container_port: 80)
|
73
102
|
|
74
|
-
expect(DeployDSLTest).to
|
75
|
-
:port_bindings,
|
76
|
-
dummy_value.merge('80/tcp' => [{ 'HostPort' => '999' }])
|
77
|
-
)
|
103
|
+
expect(DeployDSLTest.defined_service.port_bindings).to eq([Centurion::Service::PortBinding.new(666, 666, 'tcp'), Centurion::Service::PortBinding.new(999, 80, 'tcp')])
|
78
104
|
end
|
79
105
|
|
80
106
|
it 'adds new bind ports to the list with an IP binding when supplied' do
|
81
|
-
dummy_value = { '666/tcp' => ['value'] }
|
82
|
-
DeployDSLTest.set(:port_bindings, dummy_value)
|
83
107
|
DeployDSLTest.host_port(999, container_port: 80, host_ip: '0.0.0.0')
|
84
108
|
|
85
|
-
expect(DeployDSLTest).to
|
86
|
-
:port_bindings,
|
87
|
-
dummy_value.merge('80/tcp' => [{ 'HostIp' => '0.0.0.0', 'HostPort' => '999' }])
|
88
|
-
)
|
109
|
+
expect(DeployDSLTest.defined_service.port_bindings).to eq([Centurion::Service::PortBinding.new(999, 80, 'tcp', '0.0.0.0')])
|
89
110
|
end
|
90
111
|
|
91
112
|
it 'does not explode if port_bindings is empty' do
|
@@ -97,6 +118,27 @@ describe Centurion::DeployDSL do
|
|
97
118
|
end
|
98
119
|
end
|
99
120
|
|
121
|
+
describe '#network_mode' do
|
122
|
+
it 'accepts host mode' do
|
123
|
+
DeployDSLTest.network_mode('host')
|
124
|
+
expect(DeployDSLTest.defined_service.network_mode).to eq('host')
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'accepts bridge mode' do
|
128
|
+
DeployDSLTest.network_mode('bridge')
|
129
|
+
expect(DeployDSLTest.defined_service.network_mode).to eq('bridge')
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'accepts container link mode' do
|
133
|
+
DeployDSLTest.network_mode('container:a2e8937b')
|
134
|
+
expect(DeployDSLTest.defined_service.network_mode).to eq('container:a2e8937b')
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'fails when invalid mode is passed' do
|
138
|
+
expect { DeployDSLTest.network_mode('foo') }.to raise_error(SystemExit)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
100
142
|
describe '#host_volume' do
|
101
143
|
it 'raises unless passed the container_volume option' do
|
102
144
|
expect { DeployDSLTest.host_volume('foo', {}) }.to raise_error(ArgumentError, /:container_volume/)
|
@@ -110,7 +152,20 @@ describe Centurion::DeployDSL do
|
|
110
152
|
expect(DeployDSLTest.fetch(:binds)).to be_nil
|
111
153
|
DeployDSLTest.host_volume('volume1', container_volume: '/dev/sdd')
|
112
154
|
DeployDSLTest.host_volume('volume2', container_volume: '/dev/sde')
|
113
|
-
expect(DeployDSLTest.
|
155
|
+
expect(DeployDSLTest.defined_service.volumes).to eq [Centurion::Service::Volume.new('volume1', '/dev/sdd'), Centurion::Service::Volume.new('volume2', '/dev/sde')]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#extra_host' do
|
160
|
+
it 'adds new hosts to the list' do
|
161
|
+
DeployDSLTest.extra_host('192.168.33.10', 'newrelic.dev')
|
162
|
+
DeployDSLTest.extra_host('192.168.33.93', 'd.newrelic.dev')
|
163
|
+
|
164
|
+
expect(DeployDSLTest.defined_service.extra_hosts).to eq(['newrelic.dev:192.168.33.10', 'd.newrelic.dev:192.168.33.93'])
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'extra_hosts list is nil by default' do
|
168
|
+
expect(DeployDSLTest.defined_service.extra_hosts).to eq(nil)
|
114
169
|
end
|
115
170
|
end
|
116
171
|
|
@@ -120,4 +175,10 @@ describe Centurion::DeployDSL do
|
|
120
175
|
|
121
176
|
expect(DeployDSLTest.get_current_tags_for('asdf')).to eq [ { server: 'host1', tags: [ 'foo'] } ]
|
122
177
|
end
|
178
|
+
|
179
|
+
it 'appends tags to the image name when returning a service' do
|
180
|
+
DeployDSLTest.set(:tag, 'roland')
|
181
|
+
DeployDSLTest.set(:image, 'charlemagne')
|
182
|
+
expect(DeployDSLTest.defined_service.image).to eq('charlemagne:roland')
|
183
|
+
end
|
123
184
|
end
|
data/spec/deploy_spec.rb
CHANGED
@@ -6,8 +6,10 @@ describe Centurion::Deploy do
|
|
6
6
|
let(:mock_bad_status) { double('http_status_ok', status: 500) }
|
7
7
|
let(:server) { double('docker_server', attach: true, hostname: hostname) }
|
8
8
|
let(:port) { 8484 }
|
9
|
-
let(:
|
9
|
+
let(:container_id) { '21adfd2ef2ef2349494a' }
|
10
|
+
let(:container) { { 'Ports' => [{ 'PublicPort' => port }, 'Created' => Time.now.to_i ], 'Id' => container_id, 'Names' => [ 'name1' ] } }
|
10
11
|
let(:endpoint) { '/status/check' }
|
12
|
+
let(:container_id) { '21adfd2ef2ef2349494a' }
|
11
13
|
let(:test_deploy) do
|
12
14
|
Object.new.tap do |o|
|
13
15
|
o.send(:extend, Centurion::Deploy)
|
@@ -19,7 +21,6 @@ describe Centurion::Deploy do
|
|
19
21
|
|
20
22
|
before do
|
21
23
|
allow(test_deploy).to receive(:fetch).and_return nil
|
22
|
-
allow(test_deploy).to receive(:fetch).with(:container_hostname, hostname).and_return(hostname)
|
23
24
|
allow(test_deploy).to receive(:host_ip).and_return('172.16.0.1')
|
24
25
|
end
|
25
26
|
|
@@ -50,23 +51,16 @@ describe Centurion::Deploy do
|
|
50
51
|
|
51
52
|
describe '#container_up?' do
|
52
53
|
it 'recognizes when no containers are running' do
|
53
|
-
expect(server).to receive(:
|
54
|
+
expect(server).to receive(:find_container_by_id).and_return(nil)
|
54
55
|
|
55
|
-
expect(test_deploy.container_up?(server,
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'complains when more than one container is bound to this port' do
|
59
|
-
expect(server).to receive(:find_containers_by_public_port).and_return([1,2])
|
60
|
-
expect(test_deploy).to receive(:error).with /More than one container/
|
61
|
-
|
62
|
-
expect(test_deploy.container_up?(server, port)).to be_falsey
|
56
|
+
expect(test_deploy.container_up?(server, container_id)).to be_falsey
|
63
57
|
end
|
64
58
|
|
65
59
|
it 'recognizes when the container is actually running' do
|
66
|
-
expect(server).to receive(:
|
60
|
+
expect(server).to receive(:find_container_by_id).and_return(container)
|
67
61
|
expect(test_deploy).to receive(:info).with /Found container/
|
68
62
|
|
69
|
-
expect(test_deploy.container_up?(server,
|
63
|
+
expect(test_deploy.container_up?(server, container_id)).to be_truthy
|
70
64
|
end
|
71
65
|
end
|
72
66
|
|
@@ -79,7 +73,7 @@ describe Centurion::Deploy do
|
|
79
73
|
allow(test_deploy).to receive(:container_up?).and_return(true)
|
80
74
|
allow(test_deploy).to receive(:http_status_ok?).and_return(true)
|
81
75
|
|
82
|
-
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, port, '/foo', 'image_id', 'chaucer')
|
76
|
+
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, container_id, port, '/foo', 'image_id', 'chaucer')
|
83
77
|
expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
|
84
78
|
expect(test_deploy).to have_received(:info).with('Container is up!')
|
85
79
|
end
|
@@ -91,7 +85,7 @@ describe Centurion::Deploy do
|
|
91
85
|
expect(test_deploy).to receive(:exit)
|
92
86
|
expect(test_deploy).to receive(:sleep).with(0)
|
93
87
|
|
94
|
-
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, port, '/foo', 'image_id', 'chaucer', 0, 1)
|
88
|
+
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, container_id, port, '/foo', 'image_id', 'chaucer', 0, 1)
|
95
89
|
expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
|
96
90
|
end
|
97
91
|
|
@@ -102,40 +96,42 @@ describe Centurion::Deploy do
|
|
102
96
|
allow(test_deploy).to receive(:warn)
|
103
97
|
expect(test_deploy).to receive(:exit)
|
104
98
|
|
105
|
-
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, port, '/foo', 'image_id', 'chaucer', 1, 0)
|
99
|
+
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, container_id, port, '/foo', 'image_id', 'chaucer', 1, 0)
|
106
100
|
expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
|
107
101
|
end
|
108
102
|
end
|
109
103
|
|
110
104
|
describe '#cleanup_containers' do
|
111
105
|
it 'deletes all but two containers' do
|
112
|
-
|
113
|
-
|
114
|
-
{'Id' => '
|
115
|
-
{'Id' => '
|
116
|
-
{'Id' => '
|
117
|
-
{'Id' => '
|
106
|
+
service = Centurion::Service.new('walrus')
|
107
|
+
expect(server).to receive(:old_containers_for_name).with('walrus').and_return([
|
108
|
+
{'Id' => '123', 'Names' => ['walrus-3bab311b460bdf']},
|
109
|
+
{'Id' => '456', 'Names' => ['walrus-4bab311b460bdf']},
|
110
|
+
{'Id' => '789', 'Names' => ['walrus-5bab311b460bdf']},
|
111
|
+
{'Id' => '0ab', 'Names' => ['walrus-6bab311b460bdf']},
|
112
|
+
{'Id' => 'cde', 'Names' => ['walrus-7bab311b460bdf']},
|
118
113
|
])
|
119
114
|
expect(server).to receive(:remove_container).with('789')
|
120
115
|
expect(server).to receive(:remove_container).with('0ab')
|
121
116
|
expect(server).to receive(:remove_container).with('cde')
|
122
117
|
|
123
|
-
test_deploy.cleanup_containers(server,
|
118
|
+
test_deploy.cleanup_containers(server, service)
|
124
119
|
end
|
125
120
|
end
|
126
121
|
|
127
122
|
describe '#stop_containers' do
|
128
123
|
it 'calls stop_container on the right containers' do
|
124
|
+
service = Centurion::Service.new(:centurion)
|
125
|
+
service.add_port_bindings(80, 80)
|
126
|
+
|
129
127
|
second_container = container.dup
|
130
128
|
containers = [ container, second_container ]
|
131
|
-
bindings = {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]}
|
132
129
|
|
133
|
-
expect(server).to receive(:find_containers_by_public_port).and_return(containers)
|
134
|
-
expect(test_deploy).to receive(:public_port_for).with(bindings).and_return('80')
|
130
|
+
expect(server).to receive(:find_containers_by_public_port).with(80).and_return(containers)
|
135
131
|
expect(server).to receive(:stop_container).with(container['Id'], 30).once
|
136
132
|
expect(server).to receive(:stop_container).with(second_container['Id'], 30).once
|
137
133
|
|
138
|
-
test_deploy.stop_containers(server,
|
134
|
+
test_deploy.stop_containers(server, service)
|
139
135
|
end
|
140
136
|
end
|
141
137
|
|
@@ -149,348 +145,54 @@ describe Centurion::Deploy do
|
|
149
145
|
end
|
150
146
|
end
|
151
147
|
|
152
|
-
describe '#
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
let(:volumes) { nil }
|
157
|
-
let(:command) { nil }
|
158
|
-
|
159
|
-
it 'works without env_vars, port_bindings, or a command' do
|
160
|
-
config = test_deploy.container_config_for(server, image_id)
|
161
|
-
|
162
|
-
expect(config).to be_a(Hash)
|
163
|
-
expect(config.keys).to match_array(%w{ Hostname Image })
|
164
|
-
end
|
165
|
-
|
166
|
-
context 'when port bindings are specified' do
|
167
|
-
let(:port_bindings) { {1234 => 80, 9876 => 80} }
|
168
|
-
|
169
|
-
it 'sets the ExposedPorts key in the config correctly' do
|
170
|
-
config = test_deploy.container_config_for(server, image_id, port_bindings)
|
171
|
-
|
172
|
-
expect(config['ExposedPorts']).to be_a(Hash)
|
173
|
-
expect(config['ExposedPorts'].keys).to eq port_bindings.keys
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
context 'when env vars are specified' do
|
178
|
-
let(:env) { {
|
179
|
-
'FOO' => 'BAR',
|
180
|
-
'BAZ' => '%DOCKER_HOSTNAME%.example.com',
|
181
|
-
'BAR' => '%DOCKER_HOST_IP%:1234'
|
182
|
-
} }
|
183
|
-
|
184
|
-
it 'sets the Env key in the config' do
|
185
|
-
config = test_deploy.container_config_for(server, image_id, port_bindings, env)
|
186
|
-
|
187
|
-
expect(config).to be_a(Hash)
|
188
|
-
expect(config.keys).to match_array(%w{ Hostname Image Env })
|
189
|
-
expect(config['Env']).to include('FOO=BAR')
|
190
|
-
end
|
191
|
-
|
192
|
-
it 'interpolates the hostname into env_vars' do
|
193
|
-
config = test_deploy.container_config_for(server, image_id, port_bindings, env)
|
194
|
-
|
195
|
-
expect(config['Env']).to include('BAZ=host1.example.com')
|
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
|
203
|
-
end
|
204
|
-
|
205
|
-
context 'when volumes are specified' do
|
206
|
-
let(:volumes) { ["/tmp/foo:/tmp/chaucer"] }
|
207
|
-
|
208
|
-
it 'sets the Volumes key in the config' do
|
209
|
-
config = test_deploy.container_config_for(server, image_id, port_bindings, env, volumes)
|
210
|
-
|
211
|
-
expect(config).to be_a(Hash)
|
212
|
-
expect(config.keys).to match_array(%w{ Hostname Image Volumes VolumesFrom })
|
213
|
-
expect(config['Volumes']['/tmp/chaucer']).to eq({})
|
214
|
-
end
|
148
|
+
describe '#hostname_proc' do
|
149
|
+
it 'does not provide a container hostname if no override is given' do
|
150
|
+
expect(test_deploy).to receive(:fetch).with(:container_hostname).and_return nil
|
151
|
+
expect(test_deploy.hostname_proc).to be_nil
|
215
152
|
end
|
216
153
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
it 'sets the Cmd key in the config' do
|
221
|
-
config = test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command)
|
222
|
-
|
223
|
-
expect(config).to be_a(Hash)
|
224
|
-
expect(config.keys).to match_array(%w{ Hostname Image Cmd })
|
225
|
-
expect(config['Cmd']).to eq(command)
|
226
|
-
end
|
154
|
+
it 'provides container hostname if an override string is given' do
|
155
|
+
expect(test_deploy).to receive(:fetch).with(:container_hostname).and_return 'example.com'
|
156
|
+
expect(test_deploy.hostname_proc.call('foo')).to eq('example.com')
|
227
157
|
end
|
228
158
|
|
229
|
-
context '
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
before do
|
234
|
-
allow(test_deploy).to receive(:error) # Make sure we don't have red output in tests
|
235
|
-
end
|
236
|
-
|
237
|
-
it 'sets cgroup limits in the config' do
|
238
|
-
config = test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, cpu_shares)
|
239
|
-
|
240
|
-
expect(config).to be_a(Hash)
|
241
|
-
expect(config.keys).to match_array(%w{ Hostname Image Memory CpuShares })
|
242
|
-
expect(config['Memory']).to eq(10000000)
|
243
|
-
expect(config['CpuShares']).to eq(1234)
|
244
|
-
end
|
245
|
-
|
246
|
-
it 'throws a fatal error value for Cgroup Memory limit is invalid' do
|
247
|
-
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)
|
248
|
-
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, -100, cpu_shares) }.to terminate.with_code(102)
|
249
|
-
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, 0xFFFFFFFFFFFFFFFFFF, cpu_shares) }.to terminate.with_code(102)
|
250
|
-
end
|
251
|
-
|
252
|
-
it 'throws a fatal error value for Cgroup CPU limit is invalid' do
|
253
|
-
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, "I like pie") }.to terminate.with_code(101)
|
254
|
-
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, -100) }.to terminate.with_code(101)
|
255
|
-
expect { test_deploy.container_config_for(server, image_id, port_bindings, env, volumes, command, memory, 0xFFFFFFFFFFFFFFFFFF) }.to terminate.with_code(101)
|
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)
|
159
|
+
context 'container_hostname is overridden with a proc' do
|
160
|
+
it 'provides a container hostname by executing the proc given' do
|
161
|
+
expect(test_deploy).to receive(:fetch).with(:container_hostname).and_return ->(s) { "container.#{s}" }
|
162
|
+
expect(test_deploy.hostname_proc.call('example.com')).to eq('container.example.com')
|
262
163
|
end
|
263
164
|
end
|
264
165
|
end
|
265
166
|
|
266
|
-
describe '#start_container_with_config' do
|
267
|
-
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
268
|
-
|
269
|
-
it 'pass host_config to start_container' do
|
270
|
-
allow(server).to receive(:container_config_for).and_return({
|
271
|
-
'Image' => 'image_id',
|
272
|
-
'Hostname' => server.hostname,
|
273
|
-
})
|
274
|
-
|
275
|
-
allow(server).to receive(:create_container).and_return({
|
276
|
-
'Id' => 'abc123456'
|
277
|
-
})
|
278
|
-
|
279
|
-
allow(server).to receive(:inspect_container)
|
280
|
-
|
281
|
-
allow(test_deploy).to receive(:fetch).with(:custom_dns).and_return('8.8.8.8')
|
282
|
-
allow(test_deploy).to receive(:fetch).with(:name).and_return(nil)
|
283
|
-
|
284
|
-
expect(server).to receive(:start_container).with(
|
285
|
-
'abc123456',
|
286
|
-
{
|
287
|
-
'PortBindings' => bindings,
|
288
|
-
'Dns' => '8.8.8.8',
|
289
|
-
'RestartPolicy' => {
|
290
|
-
'Name' => 'on-failure',
|
291
|
-
'MaximumRetryCount' => 10
|
292
|
-
}
|
293
|
-
}
|
294
|
-
).once
|
295
|
-
|
296
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {}, nil, nil)
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
describe '#start_container_with_restart_policy' do
|
301
|
-
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
302
|
-
|
303
|
-
before do
|
304
|
-
allow(server).to receive(:container_config_for).and_return({
|
305
|
-
'Image' => 'image_id',
|
306
|
-
'Hostname' => server.hostname,
|
307
|
-
})
|
308
|
-
|
309
|
-
allow(server).to receive(:create_container).and_return({
|
310
|
-
'Id' => 'abc123456'
|
311
|
-
})
|
312
|
-
|
313
|
-
allow(server).to receive(:inspect_container)
|
314
|
-
end
|
315
|
-
|
316
|
-
it 'pass no restart policy to start_container' do
|
317
|
-
expect(server).to receive(:start_container).with(
|
318
|
-
'abc123456',
|
319
|
-
{
|
320
|
-
'PortBindings' => bindings,
|
321
|
-
'RestartPolicy' => {
|
322
|
-
'Name' => 'on-failure',
|
323
|
-
'MaximumRetryCount' => 10
|
324
|
-
}
|
325
|
-
}
|
326
|
-
).once
|
327
|
-
|
328
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {}, nil, nil)
|
329
|
-
end
|
330
|
-
|
331
|
-
it 'pass "always" restart policy to start_container' do
|
332
|
-
allow(test_deploy).to receive(:fetch).with(:restart_policy_name).and_return('always')
|
333
|
-
|
334
|
-
expect(server).to receive(:start_container).with(
|
335
|
-
'abc123456',
|
336
|
-
{
|
337
|
-
'PortBindings' => bindings,
|
338
|
-
'RestartPolicy' => {
|
339
|
-
'Name' => 'always'
|
340
|
-
}
|
341
|
-
}
|
342
|
-
).once
|
343
|
-
|
344
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {}, nil, nil)
|
345
|
-
end
|
346
|
-
|
347
|
-
it 'pass "on-failure with 50 retries" restart policy to start_container' do
|
348
|
-
allow(test_deploy).to receive(:fetch).with(:restart_policy_name).and_return('on-failure')
|
349
|
-
allow(test_deploy).to receive(:fetch).with(:restart_policy_max_retry_count).and_return(50)
|
350
|
-
|
351
|
-
expect(server).to receive(:start_container).with(
|
352
|
-
'abc123456',
|
353
|
-
{
|
354
|
-
'PortBindings' => bindings,
|
355
|
-
'RestartPolicy' => {
|
356
|
-
'Name' => 'on-failure',
|
357
|
-
'MaximumRetryCount' => 50
|
358
|
-
}
|
359
|
-
}
|
360
|
-
).once
|
361
|
-
|
362
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {}, nil, nil)
|
363
|
-
end
|
364
|
-
|
365
|
-
it 'pass "no" restart policy to start_container' do
|
366
|
-
allow(test_deploy).to receive(:fetch).with(:restart_policy_name).and_return('no')
|
367
|
-
|
368
|
-
expect(server).to receive(:start_container).with(
|
369
|
-
'abc123456',
|
370
|
-
{
|
371
|
-
'PortBindings' => bindings,
|
372
|
-
'RestartPolicy' => {
|
373
|
-
'Name' => 'no'
|
374
|
-
}
|
375
|
-
}
|
376
|
-
).once
|
377
|
-
|
378
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {}, nil, nil)
|
379
|
-
end
|
380
|
-
|
381
|
-
it 'pass "garbage" restart policy to start_container' do
|
382
|
-
allow(test_deploy).to receive(:fetch).with(:restart_policy_name).and_return('garbage')
|
383
|
-
|
384
|
-
expect(server).to receive(:start_container).with(
|
385
|
-
'abc123456',
|
386
|
-
{
|
387
|
-
'PortBindings' => bindings,
|
388
|
-
'RestartPolicy' => {
|
389
|
-
'Name' => 'on-failure',
|
390
|
-
'MaximumRetryCount' => 10
|
391
|
-
}
|
392
|
-
}
|
393
|
-
).once
|
394
|
-
|
395
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {}, nil, nil)
|
396
|
-
end
|
397
|
-
end
|
398
|
-
|
399
167
|
describe '#start_new_container' do
|
400
168
|
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
401
169
|
let(:env) { { 'FOO' => 'BAR' } }
|
402
170
|
let(:volumes) { ['/foo:/bar'] }
|
403
171
|
let(:command) { ['/bin/echo', 'hi'] }
|
404
172
|
|
405
|
-
it 'configures the container' do
|
406
|
-
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, nil, {}, nil, nil, nil).once
|
407
|
-
|
408
|
-
allow(test_deploy).to receive(:start_container_with_config)
|
409
|
-
|
410
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {}, nil)
|
411
|
-
end
|
412
|
-
|
413
|
-
it 'starts the container' do
|
414
|
-
expect(test_deploy).to receive(:start_container_with_config).with(server, {}, anything(), anything())
|
415
|
-
|
416
|
-
test_deploy.start_new_container(server, 'image_id', bindings, {})
|
417
|
-
end
|
418
|
-
|
419
|
-
it 'sets the container hostname when asked' do
|
420
|
-
allow(test_deploy).to receive(:fetch).with(:container_hostname, anything()).and_return('chaucer')
|
421
|
-
|
422
|
-
expect(server).to receive(:create_container).with(
|
423
|
-
hash_including(
|
424
|
-
'Image' => 'image_id',
|
425
|
-
'Hostname' => 'chaucer',
|
426
|
-
'ExposedPorts' => {'80/tcp'=>{}},
|
427
|
-
'Cmd' => command,
|
428
|
-
'Env' => ['FOO=BAR'],
|
429
|
-
'Volumes' => {'/bar' => {}},
|
430
|
-
),
|
431
|
-
nil
|
432
|
-
).and_return(container)
|
433
|
-
|
434
|
-
expect(server).to receive(:start_container)
|
435
|
-
expect(server).to receive(:inspect_container)
|
436
|
-
test_deploy.start_new_container(server, 'image_id', bindings, volumes, env, command)
|
437
|
-
end
|
438
|
-
|
439
173
|
it 'ultimately asks the server object to do the work' do
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
expect(server).to receive(:create_container).with(
|
444
|
-
hash_including(
|
445
|
-
'Image' => 'image_id',
|
446
|
-
'Hostname' => hostname,
|
447
|
-
'ExposedPorts' => {'80/tcp'=>{}},
|
448
|
-
'Cmd' => command,
|
449
|
-
'Env' => ['FOO=BAR'],
|
450
|
-
'Volumes' => {'/bar' => {}},
|
451
|
-
),
|
452
|
-
'app1'
|
453
|
-
).and_return(container)
|
174
|
+
service = double(:Service, name: :centurion, build_config: {"Image" => "abcdef"}, build_host_config: {})
|
175
|
+
restart_policy = double(:RestartPolicy)
|
176
|
+
|
177
|
+
expect(server).to receive(:create_container).with({"Image" => "abcdef"}, :centurion).and_return(container)
|
454
178
|
|
455
179
|
expect(server).to receive(:start_container)
|
456
180
|
expect(server).to receive(:inspect_container)
|
457
181
|
|
458
|
-
new_container = test_deploy.start_new_container(server,
|
182
|
+
new_container = test_deploy.start_new_container(server, service, restart_policy)
|
459
183
|
expect(new_container).to eq(container)
|
460
184
|
end
|
461
185
|
end
|
462
186
|
|
463
187
|
describe '#launch_console' do
|
464
|
-
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
465
|
-
let(:volumes) { nil }
|
466
|
-
let(:env) { nil }
|
467
|
-
let(:command) { nil }
|
468
|
-
let(:memory) { nil }
|
469
|
-
let(:cpu_shares) { nil }
|
470
|
-
|
471
|
-
it 'configures the container' do
|
472
|
-
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, env, volumes, command, memory, cpu_shares).once
|
473
|
-
allow(test_deploy).to receive(:start_container_with_config)
|
474
|
-
|
475
|
-
test_deploy.start_new_container(server, 'image_id', bindings, volumes, env, command, memory, cpu_shares)
|
476
|
-
end
|
477
|
-
|
478
|
-
it 'augments the container_config' do
|
479
|
-
expect(test_deploy).to receive(:start_container_with_config).with(server, volumes,
|
480
|
-
anything(),
|
481
|
-
hash_including('Cmd' => [ '/bin/bash' ], 'AttachStdin' => true , 'Tty' => true , 'OpenStdin' => true)
|
482
|
-
).and_return({'Id' => 'shakespeare'})
|
483
|
-
|
484
|
-
test_deploy.launch_console(server, 'image_id', bindings, volumes, env)
|
485
|
-
end
|
486
|
-
|
487
188
|
it 'starts the console' do
|
488
|
-
|
489
|
-
|
490
|
-
).
|
189
|
+
service = double(:Service, name: :centurion, build_console_config: {"Image" => "abcdef"}, build_host_config: {})
|
190
|
+
|
191
|
+
expect(server).to receive(:create_container).with({"Image" => "abcdef"}, :centurion).and_return(container)
|
192
|
+
expect(server).to receive(:start_container)
|
491
193
|
|
492
|
-
test_deploy.launch_console(server,
|
493
|
-
expect(server).to have_received(:attach).with(
|
194
|
+
test_deploy.launch_console(server, service)
|
195
|
+
expect(server).to have_received(:attach).with(container_id)
|
494
196
|
end
|
495
197
|
end
|
496
198
|
end
|