centurion 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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
|