rory-deploy 1.8.4.1
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/.gitignore +10 -0
- data/CONTRIBUTORS.md +77 -0
- data/Gemfile +5 -0
- data/LICENSE +19 -0
- data/README.md +574 -0
- data/Rakefile +15 -0
- data/bin/rory +80 -0
- data/bin/rory-gen-config +79 -0
- data/lib/capistrano_dsl.rb +91 -0
- data/lib/centurion.rb +9 -0
- data/lib/centurion/deploy.rb +139 -0
- data/lib/centurion/deploy_dsl.rb +180 -0
- data/lib/centurion/docker_registry.rb +89 -0
- data/lib/centurion/docker_server.rb +79 -0
- data/lib/centurion/docker_server_group.rb +33 -0
- data/lib/centurion/docker_via_api.rb +166 -0
- data/lib/centurion/docker_via_cli.rb +81 -0
- data/lib/centurion/dogestry.rb +92 -0
- data/lib/centurion/logging.rb +28 -0
- data/lib/centurion/service.rb +218 -0
- data/lib/centurion/shell.rb +46 -0
- data/lib/centurion/version.rb +3 -0
- data/lib/core_ext/numeric_bytes.rb +94 -0
- data/lib/tasks/centurion.rake +15 -0
- data/lib/tasks/deploy.rake +250 -0
- data/lib/tasks/info.rake +24 -0
- data/lib/tasks/list.rake +56 -0
- data/rory-deploy.gemspec +33 -0
- data/spec/capistrano_dsl_spec.rb +67 -0
- data/spec/deploy_dsl_spec.rb +184 -0
- data/spec/deploy_spec.rb +212 -0
- data/spec/docker_registry_spec.rb +105 -0
- data/spec/docker_server_group_spec.rb +31 -0
- data/spec/docker_server_spec.rb +92 -0
- data/spec/docker_via_api_spec.rb +246 -0
- data/spec/docker_via_cli_spec.rb +91 -0
- data/spec/dogestry_spec.rb +73 -0
- data/spec/logging_spec.rb +41 -0
- data/spec/service_spec.rb +288 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/matchers/capistrano_dsl_matchers.rb +13 -0
- data/spec/support/matchers/exit_code_matches.rb +38 -0
- metadata +214 -0
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'centurion/deploy_dsl'
|
3
|
+
require 'capistrano_dsl'
|
4
|
+
|
5
|
+
class DeployDSLTest
|
6
|
+
extend Capistrano::DSL
|
7
|
+
extend Centurion::DeployDSL
|
8
|
+
end
|
9
|
+
|
10
|
+
describe Centurion::DeployDSL do
|
11
|
+
before do
|
12
|
+
DeployDSLTest.clear_env
|
13
|
+
DeployDSLTest.set_current_environment('test')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'exposes an easy wrapper for handling each Docker host' do
|
17
|
+
recipient = double('recipient')
|
18
|
+
expect(recipient).to receive(:ping).with('host1')
|
19
|
+
expect(recipient).to receive(:ping).with('host2')
|
20
|
+
|
21
|
+
DeployDSLTest.set(:hosts, %w{ host1 host2 })
|
22
|
+
DeployDSLTest.on_each_docker_host { |h| recipient.ping(h.hostname) }
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has a DSL method for specifying the start command' do
|
26
|
+
command = %w{ /bin/echo hi }
|
27
|
+
DeployDSLTest.command command
|
28
|
+
expect(DeployDSLTest.defined_service.command).to eq(command)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'adds new env_vars to the existing ones, as strings' do
|
32
|
+
DeployDSLTest.env_vars('SHAKESPEARE' => 'Hamlet')
|
33
|
+
DeployDSLTest.env_vars('DICKENS' => 'David Copperfield',
|
34
|
+
DICKENS_BIRTH_YEAR: 1812)
|
35
|
+
|
36
|
+
expect(DeployDSLTest.defined_service.env_vars).to eq(
|
37
|
+
'SHAKESPEARE' => 'Hamlet',
|
38
|
+
'DICKENS' => 'David Copperfield',
|
39
|
+
'DICKENS_BIRTH_YEAR' => '1812'
|
40
|
+
)
|
41
|
+
end
|
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
|
+
|
73
|
+
it 'adds hosts to the host list' do
|
74
|
+
DeployDSLTest.set(:hosts, [ 'host1' ])
|
75
|
+
DeployDSLTest.host('host2')
|
76
|
+
|
77
|
+
expect(DeployDSLTest).to have_key_and_value(:hosts, %w{ host1 host2 })
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#localhost' do
|
81
|
+
it 'adds a host by reading DOCKER_HOST if present' do
|
82
|
+
expect(ENV).to receive(:[]).with('DOCKER_HOST').and_return('tcp://127.1.1.1:4240')
|
83
|
+
DeployDSLTest.localhost
|
84
|
+
expect(DeployDSLTest).to have_key_and_value(:hosts, %w[ 127.1.1.1:4240 ])
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'adds a host defaulting to loopback if DOCKER_HOST is not present' do
|
88
|
+
expect(ENV).to receive(:[]).with('DOCKER_HOST').and_return(nil)
|
89
|
+
DeployDSLTest.localhost
|
90
|
+
expect(DeployDSLTest).to have_key_and_value(:hosts, %w[ 127.0.0.1 ])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#host_port' do
|
95
|
+
it 'raises unless passed container_port in the options' do
|
96
|
+
expect { DeployDSLTest.host_port(666, {}) }.to raise_error(ArgumentError, /:container_port/)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'adds new bind ports to the list' do
|
100
|
+
DeployDSLTest.host_port(666, container_port: 666)
|
101
|
+
DeployDSLTest.host_port(999, container_port: 80)
|
102
|
+
|
103
|
+
expect(DeployDSLTest.defined_service.port_bindings).to eq([Centurion::Service::PortBinding.new(666, 666, 'tcp'), Centurion::Service::PortBinding.new(999, 80, 'tcp')])
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'adds new bind ports to the list with an IP binding when supplied' do
|
107
|
+
DeployDSLTest.host_port(999, container_port: 80, host_ip: '0.0.0.0')
|
108
|
+
|
109
|
+
expect(DeployDSLTest.defined_service.port_bindings).to eq([Centurion::Service::PortBinding.new(999, 80, 'tcp', '0.0.0.0')])
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'does not explode if port_bindings is empty' do
|
113
|
+
expect { DeployDSLTest.host_port(999, container_port: 80) }.not_to raise_error
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'raises if invalid options are passed' do
|
117
|
+
expect { DeployDSLTest.host_port(80, asdf: 'foo') }.to raise_error(ArgumentError, /invalid key!/)
|
118
|
+
end
|
119
|
+
end
|
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
|
+
|
142
|
+
describe '#host_volume' do
|
143
|
+
it 'raises unless passed the container_volume option' do
|
144
|
+
expect { DeployDSLTest.host_volume('foo', {}) }.to raise_error(ArgumentError, /:container_volume/)
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'raises when passed bogus options' do
|
148
|
+
expect { DeployDSLTest.host_volume('foo', bogus: 1) }.to raise_error(ArgumentError, /invalid key!/)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'adds new host volumes' do
|
152
|
+
expect(DeployDSLTest.fetch(:binds)).to be_nil
|
153
|
+
DeployDSLTest.host_volume('volume1', container_volume: '/dev/sdd')
|
154
|
+
DeployDSLTest.host_volume('volume2', container_volume: '/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)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'gets current tags for an image' do
|
173
|
+
allow_any_instance_of(Centurion::DockerServer).to receive(:current_tags_for).and_return([ 'foo' ])
|
174
|
+
DeployDSLTest.set(:hosts, [ 'host1' ])
|
175
|
+
|
176
|
+
expect(DeployDSLTest.get_current_tags_for('asdf')).to eq [ { server: 'host1', tags: [ 'foo'] } ]
|
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
|
184
|
+
end
|
data/spec/deploy_spec.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'centurion'
|
3
|
+
|
4
|
+
describe Centurion::Deploy do
|
5
|
+
let(:mock_ok_status) { double('http_status_ok', status: 200) }
|
6
|
+
let(:mock_bad_status) { double('http_status_ok', status: 500) }
|
7
|
+
let(:server) { double('docker_server', attach: true, hostname: hostname) }
|
8
|
+
let(:port) { 8484 }
|
9
|
+
let(:container_id) { '21adfd2ef2ef2349494a' }
|
10
|
+
let(:container) { { 'Ports' => [{ 'PublicPort' => port }, 'Created' => Time.now.to_i ], 'Id' => container_id, 'Names' => [ 'name1' ] } }
|
11
|
+
let(:endpoint) { '/status/check' }
|
12
|
+
let(:container_id) { '21adfd2ef2ef2349494a' }
|
13
|
+
let(:test_deploy) do
|
14
|
+
Object.new.tap do |o|
|
15
|
+
o.send(:extend, Centurion::Deploy)
|
16
|
+
o.send(:extend, Centurion::DeployDSL)
|
17
|
+
o.send(:extend, Centurion::Logging)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
let(:hostname) { 'host1' }
|
21
|
+
|
22
|
+
before do
|
23
|
+
allow(test_deploy).to receive(:fetch).and_return nil
|
24
|
+
allow(test_deploy).to receive(:host_ip).and_return('172.16.0.1')
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#http_status_ok?' do
|
28
|
+
it 'validates HTTP status checks when the response is good' do
|
29
|
+
expect(Excon).to receive(:get).and_return(mock_ok_status)
|
30
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_truthy
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'identifies bad HTTP responses' do
|
34
|
+
expect(Excon).to receive(:get).and_return(mock_bad_status)
|
35
|
+
allow(test_deploy).to receive(:warn)
|
36
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_falsey
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'outputs the HTTP status when it is not OK' do
|
40
|
+
expect(Excon).to receive(:get).and_return(mock_bad_status)
|
41
|
+
expect(test_deploy).to receive(:warn).with(/Got HTTP status: 500/)
|
42
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_falsey
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'handles SocketErrors and outputs a message' do
|
46
|
+
expect(Excon).to receive(:get).and_raise(Excon::Errors::SocketError.new(RuntimeError.new()))
|
47
|
+
expect(test_deploy).to receive(:warn).with(/Failed to connect/)
|
48
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_falsey
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#container_up?' do
|
53
|
+
it 'recognizes when no containers are running' do
|
54
|
+
expect(server).to receive(:find_container_by_id).and_return(nil)
|
55
|
+
|
56
|
+
expect(test_deploy.container_up?(server, container_id)).to be_falsey
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'recognizes when the container is actually running' do
|
60
|
+
expect(server).to receive(:find_container_by_id).and_return(container)
|
61
|
+
expect(test_deploy).to receive(:info).with /Found container/
|
62
|
+
|
63
|
+
expect(test_deploy.container_up?(server, container_id)).to be_truthy
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#wait_for_http_status_ok?' do
|
68
|
+
before do
|
69
|
+
allow(test_deploy).to receive(:info)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'identifies that a container is up' do
|
73
|
+
allow(test_deploy).to receive(:container_up?).and_return(true)
|
74
|
+
allow(test_deploy).to receive(:http_status_ok?).and_return(true)
|
75
|
+
|
76
|
+
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, container_id, port, '/foo', 'image_id', 'chaucer')
|
77
|
+
expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
|
78
|
+
expect(test_deploy).to have_received(:info).with('Container is up!')
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'waits when the container is not yet up' do
|
82
|
+
allow(test_deploy).to receive(:container_up?).and_return(false)
|
83
|
+
allow(test_deploy).to receive(:error)
|
84
|
+
allow(test_deploy).to receive(:warn)
|
85
|
+
expect(test_deploy).to receive(:exit)
|
86
|
+
expect(test_deploy).to receive(:sleep).with(0)
|
87
|
+
|
88
|
+
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, container_id, port, '/foo', 'image_id', 'chaucer', 0, 1)
|
89
|
+
expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'waits when the HTTP status is not OK' do
|
93
|
+
allow(test_deploy).to receive(:container_up?).and_return(true)
|
94
|
+
allow(test_deploy).to receive(:http_status_ok?).and_return(false)
|
95
|
+
allow(test_deploy).to receive(:error)
|
96
|
+
allow(test_deploy).to receive(:warn)
|
97
|
+
expect(test_deploy).to receive(:exit)
|
98
|
+
|
99
|
+
test_deploy.wait_for_health_check_ok(test_deploy.method(:http_status_ok?), server, container_id, port, '/foo', 'image_id', 'chaucer', 1, 0)
|
100
|
+
expect(test_deploy).to have_received(:info).with(/Waiting for the port/)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe '#cleanup_containers' do
|
105
|
+
it 'deletes all but two containers' do
|
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']},
|
113
|
+
])
|
114
|
+
expect(server).to receive(:remove_container).with('789')
|
115
|
+
expect(server).to receive(:remove_container).with('0ab')
|
116
|
+
expect(server).to receive(:remove_container).with('cde')
|
117
|
+
|
118
|
+
test_deploy.cleanup_containers(server, service)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#stop_containers' do
|
123
|
+
it 'calls stop_container on the right containers when ports are mapped' do
|
124
|
+
service = Centurion::Service.new(:centurion)
|
125
|
+
service.add_port_bindings(80, 80)
|
126
|
+
|
127
|
+
second_container = container.dup.tap { |c| c['Id'] = c['Id'].sub(/49494/, '55555') }
|
128
|
+
containers = [ container, second_container ]
|
129
|
+
|
130
|
+
expect(server).to receive(:find_containers_by_public_port).with(80).and_return(containers)
|
131
|
+
expect(server).to receive(:stop_container).with(container['Id'], 30).once
|
132
|
+
expect(server).to receive(:stop_container).with(second_container['Id'], 30).once
|
133
|
+
|
134
|
+
test_deploy.stop_containers(server, service)
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'calls stop_container on the right containers when ports are not mapped' do
|
138
|
+
service = Centurion::Service.new(:centurion)
|
139
|
+
|
140
|
+
second_container = container.dup
|
141
|
+
second_container = container.dup.tap { |c| c['Id'] = c['Id'].sub(/49494/, '55555') }
|
142
|
+
containers = [ container, second_container ]
|
143
|
+
|
144
|
+
expect(server).to receive(:find_containers_by_name).with(:centurion).and_return(containers)
|
145
|
+
expect(server).to receive(:stop_container).with(container['Id'], 30).once
|
146
|
+
expect(server).to receive(:stop_container).with(second_container['Id'], 30).once
|
147
|
+
|
148
|
+
test_deploy.stop_containers(server, service)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#wait_for_load_balancer_check_interval' do
|
153
|
+
it 'knows how long to sleep' do
|
154
|
+
timing = double(timing)
|
155
|
+
expect(test_deploy).to receive(:fetch).with(:rolling_deploy_check_interval, 5).and_return(timing)
|
156
|
+
expect(test_deploy).to receive(:sleep).with(timing)
|
157
|
+
|
158
|
+
test_deploy.wait_for_load_balancer_check_interval
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#hostname_proc' do
|
163
|
+
it 'does not provide a container hostname if no override is given' do
|
164
|
+
expect(test_deploy).to receive(:fetch).with(:container_hostname).and_return nil
|
165
|
+
expect(test_deploy.hostname_proc).to be_nil
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'provides container hostname if an override string is given' do
|
169
|
+
expect(test_deploy).to receive(:fetch).with(:container_hostname).and_return 'example.com'
|
170
|
+
expect(test_deploy.hostname_proc.call('foo')).to eq('example.com')
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'container_hostname is overridden with a proc' do
|
174
|
+
it 'provides a container hostname by executing the proc given' do
|
175
|
+
expect(test_deploy).to receive(:fetch).with(:container_hostname).and_return ->(s) { "container.#{s}" }
|
176
|
+
expect(test_deploy.hostname_proc.call('example.com')).to eq('container.example.com')
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
describe '#start_new_container' do
|
182
|
+
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
183
|
+
let(:env) { { 'FOO' => 'BAR' } }
|
184
|
+
let(:volumes) { ['/foo:/bar'] }
|
185
|
+
let(:command) { ['/bin/echo', 'hi'] }
|
186
|
+
|
187
|
+
it 'ultimately asks the server object to do the work' do
|
188
|
+
service = double(:Service, name: :centurion, build_config: {"Image" => "abcdef"}, build_host_config: {})
|
189
|
+
restart_policy = double(:RestartPolicy)
|
190
|
+
|
191
|
+
expect(server).to receive(:create_container).with({"Image" => "abcdef"}, :centurion).and_return(container)
|
192
|
+
|
193
|
+
expect(server).to receive(:start_container)
|
194
|
+
expect(server).to receive(:inspect_container)
|
195
|
+
|
196
|
+
new_container = test_deploy.start_new_container(server, service, restart_policy)
|
197
|
+
expect(new_container).to eq(container)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe '#launch_console' do
|
202
|
+
it 'starts the console' do
|
203
|
+
service = double(:Service, name: :centurion, build_console_config: {"Image" => "abcdef"}, build_host_config: {})
|
204
|
+
|
205
|
+
expect(server).to receive(:create_container).with({"Image" => "abcdef"}, :centurion).and_return(container)
|
206
|
+
expect(server).to receive(:start_container)
|
207
|
+
|
208
|
+
test_deploy.launch_console(server, service)
|
209
|
+
expect(server).to have_received(:attach).with(container_id)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'centurion/docker_registry'
|
3
|
+
|
4
|
+
describe Centurion::DockerRegistry do
|
5
|
+
let(:registry_url) { 'http://localhost/' }
|
6
|
+
let(:registry) { Centurion::DockerRegistry.new(registry_url) }
|
7
|
+
|
8
|
+
describe '#repository_tags' do
|
9
|
+
let(:repository) { 'foobar' }
|
10
|
+
let(:tag_name) { 'arbitrary_tag' }
|
11
|
+
let(:image_id) { 'deadbeef0000' }
|
12
|
+
let(:url) { any_args() }
|
13
|
+
|
14
|
+
before do
|
15
|
+
expect(Excon).to receive(:get).with(url).and_return(
|
16
|
+
double(status: 200, body: response)
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
subject { registry.repository_tags(repository) }
|
21
|
+
|
22
|
+
describe 'handling different responses from open source and official registries' do
|
23
|
+
context 'when given a response from the official Docker registry' do
|
24
|
+
let(:registry_url) { Centurion::DockerRegistry::OFFICIAL_URL }
|
25
|
+
let(:response) { <<-JSON.strip }
|
26
|
+
[{"layer": "#{image_id}", "name": "#{tag_name}"}]
|
27
|
+
JSON
|
28
|
+
|
29
|
+
it 'normalizes the response' do
|
30
|
+
expect(subject).to eq(tag_name => image_id)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when given a response from the open-source Docker registry' do
|
35
|
+
let(:response) { <<-JSON.strip }
|
36
|
+
{"#{tag_name}": "#{image_id}"}
|
37
|
+
JSON
|
38
|
+
|
39
|
+
it 'normalizes the response' do
|
40
|
+
expect(subject).to eq(tag_name => image_id)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when given the official Docker registry and a repository with a hostname' do
|
46
|
+
let(:registry_url) { Centurion::DockerRegistry::OFFICIAL_URL }
|
47
|
+
let(:repository) { 'docker-reg.example.com/foobar' }
|
48
|
+
let(:response) { <<-JSON.strip }
|
49
|
+
[{"layer": "#{image_id}", "name": "#{tag_name}"}]
|
50
|
+
JSON
|
51
|
+
|
52
|
+
it 'fetches from the image-referenced registry' do
|
53
|
+
expect(subject).to eq(tag_name => image_id)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when given any other registry' do
|
58
|
+
let(:registry_url) { 'http://my-registry.example.com' }
|
59
|
+
let(:response) { <<-JSON.strip }
|
60
|
+
{"#{tag_name}": "#{image_id}"}
|
61
|
+
JSON
|
62
|
+
|
63
|
+
context 'and a repository with a hostname' do
|
64
|
+
let(:repository) { 'docker-reg.example.com/foobar' }
|
65
|
+
|
66
|
+
it 'fetches from the image-referenced registry' do
|
67
|
+
expect(subject).to eq(tag_name => image_id)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'and a repository with no hostname' do
|
72
|
+
let(:repository) { 'foobar' }
|
73
|
+
|
74
|
+
it 'fetches from the image-referenced registry' do
|
75
|
+
expect(subject).to eq(tag_name => image_id)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#repository_auth' do
|
82
|
+
let(:tag_name) { 'arbitrary_tag' }
|
83
|
+
let(:image_id) { 'deadbeef0000' }
|
84
|
+
let(:user) { 'user_foo' }
|
85
|
+
let(:password) { 'pass_bar' }
|
86
|
+
let(:registry) { Centurion::DockerRegistry.new(registry_url, user, password) }
|
87
|
+
|
88
|
+
context 'when authentication data is provided to the DockerRegistry object' do
|
89
|
+
let(:registry_url) { Centurion::DockerRegistry::OFFICIAL_URL }
|
90
|
+
let(:repository) { 'docker-reg.example.com/foobar' }
|
91
|
+
let(:response) { <<-JSON.strip }
|
92
|
+
[{"layer": "#{image_id}", "name": "#{tag_name}"}]
|
93
|
+
JSON
|
94
|
+
|
95
|
+
before do
|
96
|
+
expect(Excon).to receive(:get).with(kind_of(String), hash_including(user: user, password: password)).and_return(
|
97
|
+
double(status: 200, body: response)
|
98
|
+
)
|
99
|
+
end
|
100
|
+
it 'uses it to connect to the registry' do
|
101
|
+
registry.repository_tags(repository)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|