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.
@@ -1,3 +1,3 @@
1
1
  module Centurion
2
- VERSION = '1.6.0'
2
+ VERSION = '1.8.0'
3
3
  end
@@ -1,4 +1,4 @@
1
- # Copied from ActiveSupport 4.2.1 lib/active_support/core_ext/numeric/bytes.rb
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
- 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
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
- # 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
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
- # 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
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
- # 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
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
- # 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
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
- # 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
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
- # 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
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
+
@@ -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
@@ -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, fetch(:port_bindings), fetch(:stop_timeout, 30))
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
- stop_containers(server, fetch(:port_bindings), fetch(:stop_timeout, 30))
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
- skip_ports = Array(fetch(:rolling_deploy_skip_ports, [])).map(&:to_s)
118
+ stop_containers(server, service, fetch(:stop_timeout, 30))
129
119
 
130
- fetch(:port_bindings).each_pair do |container_port, host_ports|
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 |target_server|
152
- cleanup_containers(target_server, fetch(:port_bindings))
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
- hosts, docker_path = fetch(:hosts, []), fetch(:docker_path)
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
@@ -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 = ['/bin/echo', 'hi']
26
+ command = %w{ /bin/echo hi }
27
27
  DeployDSLTest.command command
28
- expect(DeployDSLTest.fetch(:command)).to equal(command)
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.set(:env_vars, { 'SHAKESPEARE' => 'Hamlet' })
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.fetch(:env_vars)).to include(
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
- dummy_value = { '666/tcp' => ['value'] }
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 have_key_and_value(
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 have_key_and_value(
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.fetch(:binds)).to eq %w{ volume1:/dev/sdd volume2:/dev/sde }
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
@@ -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(:container) { { 'Ports' => [{ 'PublicPort' => port }, 'Created' => Time.now.to_i ], 'Id' => '21adfd2ef2ef2349494a', 'Names' => [ 'name1' ] } }
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(:find_containers_by_public_port).and_return([])
54
+ expect(server).to receive(:find_container_by_id).and_return(nil)
54
55
 
55
- expect(test_deploy.container_up?(server, port)).to be_falsey
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(:find_containers_by_public_port).and_return([container])
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, port)).to be_truthy
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
- expect(server).to receive(:old_containers_for_port).with(port.to_s).and_return([
113
- {'Id' => '123', 'Names' => ['foo']},
114
- {'Id' => '456', 'Names' => ['foo']},
115
- {'Id' => '789', 'Names' => ['foo']},
116
- {'Id' => '0ab', 'Names' => ['foo']},
117
- {'Id' => 'cde', 'Names' => ['foo']},
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, {'80/tcp' => [{'HostIp' => '0.0.0.0', 'HostPort' => port.to_s}]})
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, bindings)
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 '#container_config_for' do
153
- let(:image_id) { 'image_id' }
154
- let(:port_bindings) { nil }
155
- let(:env) { nil }
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
- context 'when a custom command is specified' do
218
- let(:command) { ['/bin/echo', 'hi'] }
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 'when cgroup limits are specified' do
230
- let(:memory) { 10000000 }
231
- let(:cpu_shares) { 1234 }
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
- allow(test_deploy).to receive(:fetch).with(:custom_dns).and_return(nil)
441
- allow(test_deploy).to receive(:fetch).with(:name).and_return('app1')
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, 'image_id', bindings, volumes, env, command)
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
- expect(test_deploy).to receive(:start_container_with_config).with(
489
- server, nil, anything(), anything()
490
- ).and_return({'Id' => 'shakespeare'})
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, 'image_id', bindings, volumes, env)
493
- expect(server).to have_received(:attach).with('shakespeare')
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