centurion 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/CONTRIBUTORS.md +42 -0
- data/Gemfile +6 -0
- data/LICENSE +19 -0
- data/README.md +239 -0
- data/Rakefile +15 -0
- data/bin/centurion +70 -0
- data/bin/centurionize +60 -0
- data/centurion.gemspec +40 -0
- data/lib/capistrano_dsl.rb +91 -0
- data/lib/centurion.rb +5 -0
- data/lib/centurion/deploy.rb +145 -0
- data/lib/centurion/deploy_dsl.rb +94 -0
- data/lib/centurion/docker_registry.rb +35 -0
- data/lib/centurion/docker_server.rb +58 -0
- data/lib/centurion/docker_server_group.rb +31 -0
- data/lib/centurion/docker_via_api.rb +121 -0
- data/lib/centurion/docker_via_cli.rb +71 -0
- data/lib/centurion/logging.rb +28 -0
- data/lib/centurion/version.rb +3 -0
- data/lib/tasks/deploy.rake +177 -0
- data/lib/tasks/info.rake +24 -0
- data/lib/tasks/list.rake +52 -0
- data/spec/capistrano_dsl_spec.rb +67 -0
- data/spec/deploy_dsl_spec.rb +104 -0
- data/spec/deploy_spec.rb +220 -0
- data/spec/docker_server_group_spec.rb +31 -0
- data/spec/docker_server_spec.rb +43 -0
- data/spec/docker_via_api_spec.rb +111 -0
- data/spec/docker_via_cli_spec.rb +42 -0
- data/spec/logging_spec.rb +41 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/matchers/capistrano_dsl_matchers.rb +13 -0
- metadata +243 -0
data/lib/tasks/list.rake
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'centurion/docker_registry'
|
2
|
+
|
3
|
+
task :list do
|
4
|
+
invoke 'list:tags'
|
5
|
+
invoke 'list:running_containers'
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :list do
|
9
|
+
task :running_container_tags do
|
10
|
+
|
11
|
+
tags = get_current_tags_for(fetch(:image))
|
12
|
+
|
13
|
+
$stderr.puts "\n\nCurrent #{current_environment} tags for #{fetch(:image)}:\n\n"
|
14
|
+
tags.each do |info|
|
15
|
+
if info && !info[:tags].empty?
|
16
|
+
$stderr.puts "#{'%-20s' % info[:server]}: #{info[:tags].join(', ')}"
|
17
|
+
else
|
18
|
+
$stderr.puts "#{'%-20s' % info[:server]}: NO TAGS!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
$stderr.puts "\nAll tags for this image: #{tags.map { |t| t[:tags] }.flatten.uniq.join(', ')}"
|
23
|
+
end
|
24
|
+
|
25
|
+
task :tags do
|
26
|
+
begin
|
27
|
+
registry = Centurion::DockerRegistry.new()
|
28
|
+
tags = registry.respository_tags(fetch(:image))
|
29
|
+
tags.each do |tag|
|
30
|
+
puts "\t#{tag[0]}\t-> #{tag[1][0..11]}"
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
error "Couldn't communicate with Registry: #{e.message}"
|
34
|
+
end
|
35
|
+
puts
|
36
|
+
end
|
37
|
+
|
38
|
+
task :running_containers do
|
39
|
+
on_each_docker_host do |target_server|
|
40
|
+
begin
|
41
|
+
running_containers = target_server.ps
|
42
|
+
running_containers.each do |container|
|
43
|
+
puts container.inspect
|
44
|
+
end
|
45
|
+
rescue StandardError => e
|
46
|
+
error "Couldn't communicate with Docker on #{target_server.hostname}: #{e.message}"
|
47
|
+
raise
|
48
|
+
end
|
49
|
+
puts
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'capistrano_dsl'
|
3
|
+
|
4
|
+
class DSLTest
|
5
|
+
extend Capistrano::DSL
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Capistrano::DSL do
|
9
|
+
before do
|
10
|
+
DSLTest.clear_env
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'handling multiple environments' do
|
14
|
+
it 'sets the environment' do
|
15
|
+
expect { DSLTest.set_current_environment(:test) }.not_to raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'fetchs the current environment' do
|
19
|
+
DSLTest.set_current_environment(:test)
|
20
|
+
expect(DSLTest.current_environment).to eq(:test)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'without a current environment set' do
|
25
|
+
it 'dies if the current_environment is not set' do
|
26
|
+
expect { DSLTest.set(:foo, 'asdf') }.to raise_error(Capistrano::DSL::CurrentEnvironmentNotSetError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with a current environment set' do
|
31
|
+
before do
|
32
|
+
DSLTest.set_current_environment(:test)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'stores variables in the environment' do
|
36
|
+
expect { DSLTest.set(:foo, 'bar') }.not_to raise_error
|
37
|
+
expect(DSLTest).to have_key_and_value(:foo, 'bar')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'deletes keys from the environment' do
|
41
|
+
DSLTest.set(:foo, 'bar')
|
42
|
+
expect(DSLTest).to have_key_and_value(:foo, 'bar')
|
43
|
+
DSLTest.delete(:foo)
|
44
|
+
expect(DSLTest.fetch(:foo)).to be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns true for any? when the value exists' do
|
48
|
+
DSLTest.set(:foo, 'bar')
|
49
|
+
expect(DSLTest.any?(:foo)).to be_true
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns false for any? when the value does not exist' do
|
53
|
+
expect(DSLTest.any?(:foo)).to be_false
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'passes through the any? method to values that support it' do
|
57
|
+
class NoAny
|
58
|
+
def any?
|
59
|
+
'oh no'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
DSLTest.set(:foo, NoAny.new)
|
64
|
+
DSLTest.any?(:foo).should eq('oh no')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,104 @@
|
|
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 'adds new env_vars to the existing ones' do
|
26
|
+
DeployDSLTest.set(:env_vars, { 'SHAKESPEARE' => 'Hamlet' })
|
27
|
+
DeployDSLTest.env_vars('DICKENS' => 'David Copperfield')
|
28
|
+
|
29
|
+
expect(DeployDSLTest.fetch(:env_vars)).to include(
|
30
|
+
'SHAKESPEARE' => 'Hamlet',
|
31
|
+
'DICKENS' => 'David Copperfield'
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'adds hosts to the host list' do
|
36
|
+
DeployDSLTest.set(:hosts, [ 'host1' ])
|
37
|
+
DeployDSLTest.host('host2')
|
38
|
+
|
39
|
+
expect(DeployDSLTest).to have_key_and_value(:hosts, %w{ host1 host2 })
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#localhost' do
|
43
|
+
it 'adds a host by reading DOCKER_HOST if present' do
|
44
|
+
expect(ENV).to receive(:[]).with('DOCKER_HOST').and_return('tcp://127.1.1.1:4240')
|
45
|
+
DeployDSLTest.localhost
|
46
|
+
expect(DeployDSLTest).to have_key_and_value(:hosts, %w[ 127.1.1.1:4240 ])
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'adds a host defaulting to loopback if DOCKER_HOST is not present' do
|
50
|
+
expect(ENV).to receive(:[]).with('DOCKER_HOST').and_return(nil)
|
51
|
+
DeployDSLTest.localhost
|
52
|
+
expect(DeployDSLTest).to have_key_and_value(:hosts, %w[ 127.0.0.1 ])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#host_port' do
|
57
|
+
it 'raises unless passed container_port in the options' do
|
58
|
+
expect { DeployDSLTest.host_port(666, {}) }.to raise_error(ArgumentError, /:container_port/)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'adds new bind ports to the list' do
|
62
|
+
dummy_value = { '666/tcp' => ['value'] }
|
63
|
+
DeployDSLTest.set(:port_bindings, dummy_value)
|
64
|
+
DeployDSLTest.host_port(999, container_port: 80)
|
65
|
+
|
66
|
+
expect(DeployDSLTest).to have_key_and_value(
|
67
|
+
:port_bindings,
|
68
|
+
dummy_value.merge('80/tcp' => [{ 'HostIp' => '0.0.0.0', 'HostPort' => '999' }])
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does not explode if port_bindings is empty' do
|
73
|
+
expect { DeployDSLTest.host_port(999, container_port: 80) }.not_to raise_error
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'raises if invalid options are passed' do
|
77
|
+
expect { DeployDSLTest.host_port(80, asdf: 'foo') }.to raise_error(ArgumentError, /invalid key!/)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#host_volume' do
|
82
|
+
it 'raises unless passed the container_volume option' do
|
83
|
+
expect { DeployDSLTest.host_volume('foo', {}) }.to raise_error(ArgumentError, /:container_volume/)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'raises when passed bogus options' do
|
87
|
+
expect { DeployDSLTest.host_volume('foo', bogus: 1) }.to raise_error(ArgumentError, /invalid key!/)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'adds new host volumes' do
|
91
|
+
expect(DeployDSLTest.fetch(:binds)).to be_nil
|
92
|
+
DeployDSLTest.host_volume('volume1', container_volume: '/dev/sdd')
|
93
|
+
DeployDSLTest.host_volume('volume2', container_volume: '/dev/sde')
|
94
|
+
expect(DeployDSLTest.fetch(:binds)).to eq %w{ volume1:/dev/sdd volume2:/dev/sde }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'gets current tags for an image' do
|
99
|
+
Centurion::DockerServer.any_instance.stub(current_tags_for: [ 'foo' ])
|
100
|
+
DeployDSLTest.set(:hosts, [ 'host1' ])
|
101
|
+
|
102
|
+
expect(DeployDSLTest.get_current_tags_for('asdf')).to eq [ { server: 'host1', tags: [ 'foo'] } ]
|
103
|
+
end
|
104
|
+
end
|
data/spec/deploy_spec.rb
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'centurion/deploy'
|
2
|
+
require 'centurion/deploy_dsl'
|
3
|
+
require 'centurion/logging'
|
4
|
+
|
5
|
+
describe Centurion::Deploy do
|
6
|
+
let(:mock_ok_status) { double('http_status_ok').tap { |s| s.stub(status: 200) } }
|
7
|
+
let(:mock_bad_status) { double('http_status_ok').tap { |s| s.stub(status: 500) } }
|
8
|
+
let(:server) { double('docker_server').tap { |s| s.stub(hostname: 'host1'); s.stub(:attach) } }
|
9
|
+
let(:port) { 8484 }
|
10
|
+
let(:container) { { 'Ports' => [{ 'PublicPort' => port }, 'Created' => Time.now.to_i ], 'Id' => '21adfd2ef2ef2349494a', 'Names' => [ 'name1' ] } }
|
11
|
+
let(:endpoint) { '/status/check' }
|
12
|
+
let(:test_deploy) do
|
13
|
+
Object.new.tap do |o|
|
14
|
+
o.send(:extend, Centurion::Deploy)
|
15
|
+
o.send(:extend, Centurion::DeployDSL)
|
16
|
+
o.send(:extend, Centurion::Logging)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#http_status_ok?' do
|
21
|
+
it 'validates HTTP status checks when the response is good' do
|
22
|
+
expect(Excon).to receive(:get).and_return(mock_ok_status)
|
23
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'identifies bad HTTP responses' do
|
27
|
+
expect(Excon).to receive(:get).and_return(mock_bad_status)
|
28
|
+
test_deploy.stub(:warn)
|
29
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_false
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'outputs the HTTP status when it is not OK' do
|
33
|
+
expect(Excon).to receive(:get).and_return(mock_bad_status)
|
34
|
+
expect(test_deploy).to receive(:warn).with(/Got HTTP status: 500/)
|
35
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'handles SocketErrors and outputs a message' do
|
39
|
+
expect(Excon).to receive(:get).and_raise(Excon::Errors::SocketError.new(RuntimeError.new()))
|
40
|
+
expect(test_deploy).to receive(:warn).with(/Failed to connect/)
|
41
|
+
expect(test_deploy.http_status_ok?(server, port, endpoint)).to be_false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#container_up?' do
|
46
|
+
it 'recognizes when no containers are running' do
|
47
|
+
expect(server).to receive(:find_containers_by_public_port).and_return([])
|
48
|
+
|
49
|
+
test_deploy.container_up?(server, port).should be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'complains when more than one container is bound to this port' do
|
53
|
+
expect(server).to receive(:find_containers_by_public_port).and_return([1,2])
|
54
|
+
expect(test_deploy).to receive(:error).with /More than one container/
|
55
|
+
|
56
|
+
test_deploy.container_up?(server, port).should be_false
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'recognizes when the container is actually running' do
|
60
|
+
expect(server).to receive(:find_containers_by_public_port).and_return([container])
|
61
|
+
expect(test_deploy).to receive(:info).with /Found container/
|
62
|
+
|
63
|
+
test_deploy.container_up?(server, port).should be_true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#wait_for_http_status_ok?' do
|
68
|
+
before do
|
69
|
+
test_deploy.stub(:info)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'identifies that a container is up' do
|
73
|
+
test_deploy.stub(:container_up? => true)
|
74
|
+
test_deploy.stub(:http_status_ok? => true)
|
75
|
+
|
76
|
+
test_deploy.wait_for_http_status_ok(server, 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
|
+
test_deploy.stub(:container_up? => false)
|
83
|
+
test_deploy.stub(:error)
|
84
|
+
test_deploy.stub(:warn)
|
85
|
+
expect(test_deploy).to receive(:exit)
|
86
|
+
expect(test_deploy).to receive(:sleep).with(0)
|
87
|
+
|
88
|
+
test_deploy.wait_for_http_status_ok(server, 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
|
+
test_deploy.stub(:container_up? => true)
|
94
|
+
test_deploy.stub(:http_status_ok? => false)
|
95
|
+
test_deploy.stub(:error)
|
96
|
+
test_deploy.stub(:warn)
|
97
|
+
expect(test_deploy).to receive(:exit)
|
98
|
+
|
99
|
+
test_deploy.wait_for_http_status_ok(server, 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
|
+
expect(server).to receive(:old_containers_for_port).with(port.to_s).and_return([
|
107
|
+
{'Id' => '123', 'Names' => ['foo']},
|
108
|
+
{'Id' => '456', 'Names' => ['foo']},
|
109
|
+
{'Id' => '789', 'Names' => ['foo']},
|
110
|
+
{'Id' => '0ab', 'Names' => ['foo']},
|
111
|
+
{'Id' => 'cde', 'Names' => ['foo']},
|
112
|
+
])
|
113
|
+
expect(server).to receive(:remove_container).with('789')
|
114
|
+
expect(server).to receive(:remove_container).with('0ab')
|
115
|
+
expect(server).to receive(:remove_container).with('cde')
|
116
|
+
|
117
|
+
test_deploy.cleanup_containers(server, {'80/tcp' => [{'HostIp' => '0.0.0.0', 'HostPort' => port.to_s}]})
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#stop_containers' do
|
122
|
+
it 'calls stop_container on the right containers' do
|
123
|
+
second_container = container.dup
|
124
|
+
containers = [ container, second_container ]
|
125
|
+
bindings = {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]}
|
126
|
+
|
127
|
+
expect(server).to receive(:find_containers_by_public_port).and_return(containers)
|
128
|
+
expect(test_deploy).to receive(:public_port_for).with(bindings).and_return('80')
|
129
|
+
expect(server).to receive(:stop_container).with(container['Id']).once
|
130
|
+
expect(server).to receive(:stop_container).with(second_container['Id']).once
|
131
|
+
|
132
|
+
test_deploy.stop_containers(server, bindings)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe '#wait_for_load_balancer_check_interval' do
|
137
|
+
it 'knows how long to sleep' do
|
138
|
+
timing = double(timing)
|
139
|
+
expect(test_deploy).to receive(:fetch).with(:rolling_deploy_check_interval, 5).and_return(timing)
|
140
|
+
expect(test_deploy).to receive(:sleep).with(timing)
|
141
|
+
|
142
|
+
test_deploy.wait_for_load_balancer_check_interval
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
describe '#container_config_for' do
|
147
|
+
it 'works with env_vars provided' do
|
148
|
+
config = test_deploy.container_config_for(server, 'image_id', {}, 'FOO' => 'BAR')
|
149
|
+
|
150
|
+
expect(config).to be_a(Hash)
|
151
|
+
expect(config.keys).to match_array(%w{ Hostname Image Env ExposedPorts })
|
152
|
+
expect(config['Env']).to eq(['FOO=BAR'])
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'works without env_vars or port_bindings' do
|
156
|
+
config = test_deploy.container_config_for(server, 'image_id')
|
157
|
+
|
158
|
+
expect(config).to be_a(Hash)
|
159
|
+
expect(config.keys).to match_array(%w{ Hostname Image })
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#start_new_container' do
|
164
|
+
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
165
|
+
|
166
|
+
it 'configures the container' do
|
167
|
+
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, nil).once
|
168
|
+
test_deploy.stub(:start_container_with_config)
|
169
|
+
|
170
|
+
test_deploy.start_new_container(server, 'image_id', bindings, {})
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'starts the container' do
|
174
|
+
expect(test_deploy).to receive(:start_container_with_config).with(server, {}, anything(), anything())
|
175
|
+
|
176
|
+
test_deploy.start_new_container(server, 'image_id', bindings, {})
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'ultimately asks the server object to do the work' do
|
180
|
+
server.should_receive(:create_container).with(
|
181
|
+
hash_including({'Image'=>'image_id', 'Hostname'=>'host1', 'ExposedPorts'=>{'80/tcp'=>{}}})
|
182
|
+
).and_return(container)
|
183
|
+
|
184
|
+
server.should_receive(:start_container)
|
185
|
+
server.should_receive(:inspect_container)
|
186
|
+
|
187
|
+
new_container = test_deploy.start_new_container(server, 'image_id', bindings, {})
|
188
|
+
expect(new_container).to eq(container)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '#launch_console' do
|
193
|
+
let(:bindings) { {'80/tcp'=>[{'HostIp'=>'0.0.0.0', 'HostPort'=>'80'}]} }
|
194
|
+
|
195
|
+
it 'configures the container' do
|
196
|
+
expect(test_deploy).to receive(:container_config_for).with(server, 'image_id', bindings, nil).once
|
197
|
+
test_deploy.stub(:start_container_with_config)
|
198
|
+
|
199
|
+
test_deploy.start_new_container(server, 'image_id', bindings, {})
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'augments the container_config' do
|
203
|
+
expect(test_deploy).to receive(:start_container_with_config).with(server, {},
|
204
|
+
anything(),
|
205
|
+
hash_including('Cmd' => [ '/bin/bash' ], 'AttachStdin' => true , 'Tty' => true , 'OpenStdin' => true)
|
206
|
+
).and_return({'Id' => 'shakespeare'})
|
207
|
+
|
208
|
+
test_deploy.launch_console(server, 'image_id', bindings, {})
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'starts the console' do
|
212
|
+
expect(test_deploy).to receive(:start_container_with_config).with(
|
213
|
+
server, {}, anything(), anything()
|
214
|
+
).and_return({'Id' => 'shakespeare'})
|
215
|
+
|
216
|
+
test_deploy.launch_console(server, 'image_id', bindings, {})
|
217
|
+
expect(server).to have_received(:attach).with('shakespeare')
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'centurion/docker_server'
|
3
|
+
require 'centurion/docker_server_group'
|
4
|
+
|
5
|
+
describe Centurion::DockerServerGroup do
|
6
|
+
let(:docker_path) { 'docker' }
|
7
|
+
let(:group) { Centurion::DockerServerGroup.new(['host1', 'host2'], docker_path) }
|
8
|
+
|
9
|
+
it 'takes a hostlist and instantiates DockerServers' do
|
10
|
+
expect(group.hosts).to have(2).items
|
11
|
+
expect(group.hosts.first).to be_a(Centurion::DockerServer)
|
12
|
+
expect(group.hosts.last).to be_a(Centurion::DockerServer)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'implements Enumerable' do
|
16
|
+
expect(group.methods).to be_a_kind_of(Enumerable)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'prints a friendly message to stderr when iterating' do
|
20
|
+
expect(group).to receive(:info).with(/Connecting to Docker on host[0-9]/).twice
|
21
|
+
|
22
|
+
group.each { |host| }
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can run parallel operations' do
|
26
|
+
item = double('item').tap { |i| i.stub(:dummy_method) }
|
27
|
+
expect(item).to receive(:dummy_method).twice
|
28
|
+
|
29
|
+
expect { group.each_in_parallel { |host| item.dummy_method } }.not_to raise_error
|
30
|
+
end
|
31
|
+
end
|