bigrig 0.0.0 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +84 -0
- data/LICENSE +28 -0
- data/README.md +191 -0
- data/Rakefile +10 -0
- data/bigrig.gemspec +29 -0
- data/bin/bigrig +113 -0
- data/lib/bigrig/actions/destroy_action.rb +21 -0
- data/lib/bigrig/actions/dev_action.rb +19 -0
- data/lib/bigrig/actions/log_action.rb +45 -0
- data/lib/bigrig/actions/run_action.rb +14 -0
- data/lib/bigrig/actions/ship_action.rb +51 -0
- data/lib/bigrig/actions.rb +5 -0
- data/lib/bigrig/dependency_graph.rb +21 -0
- data/lib/bigrig/descriptor.rb +43 -0
- data/lib/bigrig/docker_adapter.rb +129 -0
- data/lib/bigrig/models/application.rb +22 -0
- data/lib/bigrig/models/base_model.rb +7 -0
- data/lib/bigrig/models/container.rb +40 -0
- data/lib/bigrig/models.rb +3 -0
- data/lib/bigrig/output_parser.rb +52 -0
- data/lib/bigrig/runner.rb +94 -0
- data/lib/bigrig/version.rb +3 -0
- data/lib/bigrig.rb +8 -0
- data/spec/bigrig/actions/destroy_action_spec.rb +55 -0
- data/spec/bigrig/actions/dev_action_spec.rb +17 -0
- data/spec/bigrig/actions/log_action_spec.rb +33 -0
- data/spec/bigrig/actions/run_action_spec.rb +270 -0
- data/spec/bigrig/actions/ship_action_spec.rb +82 -0
- data/spec/bigrig/dependency_graph_spec.rb +19 -0
- data/spec/bigrig/descriptor_spec.rb +56 -0
- data/spec/bigrig/docker_adapter_spec.rb +409 -0
- data/spec/bigrig/models/application_spec.rb +32 -0
- data/spec/bigrig/models/container_spec.rb +115 -0
- data/spec/bigrig/output_parser_spec.rb +71 -0
- data/spec/bigrig_spec.rb +249 -0
- data/spec/data/addscontainer.json +24 -0
- data/spec/data/build/Dockerfile +2 -0
- data/spec/data/build/test +0 -0
- data/spec/data/dev.json +26 -0
- data/spec/data/duplicate.json +13 -0
- data/spec/data/env.json +12 -0
- data/spec/data/hosts_ip.json +12 -0
- data/spec/data/hosts_name.json +11 -0
- data/spec/data/links.json +13 -0
- data/spec/data/log.json +8 -0
- data/spec/data/multiple.json +13 -0
- data/spec/data/path.json +5 -0
- data/spec/data/ports.json +9 -0
- data/spec/data/profiles.json +24 -0
- data/spec/data/ship.json +5 -0
- data/spec/data/single.json +8 -0
- data/spec/data/tagandpath.json +24 -0
- data/spec/data/tiny-image.tar +0 -0
- data/spec/data/volumes.json +13 -0
- data/spec/spec_helper.rb +104 -0
- data/spec/support/bigrig_vcr +8 -0
- data/spec/support/vcr.rb +15 -0
- data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_has_exited/should_remove_the_container.yml +392 -0
- data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_is_running/kills_and_removes_the_container.yml +418 -0
- data/spec/vcr/Bigrig_DockerAdapter/_build/builds_the_given_directory.yml +63 -0
- data/spec/vcr/Bigrig_DockerAdapter/_build/passes_build_input_to_a_block.yml +35 -0
- data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_does_not_exist/is_false.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_exists/is_true.yml +82 -0
- data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_does_not_exist/raise_a_ImageNotFoundError.yml +172 -0
- data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_exists/returns_the_image_id.yml +148 -0
- data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_does_not_exist/should_raise_an_error.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_is_running/should_kill_the_container.yml +200 -0
- data/spec/vcr/Bigrig_DockerAdapter/_logs/streams_logs_to_a_block.yml +163 -0
- data/spec/vcr/Bigrig_DockerAdapter/_pull/given_a_block_to_capture_output/should_capture_output.yml +64 -0
- data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_does_not_exist/raises_a_RepoNotFoundError.yml +30 -0
- data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_exists/returns_the_image_id.yml +92 -0
- data/spec/vcr/Bigrig_DockerAdapter/_push/given_credentials/will_pass_login_and_password.yml +254 -0
- data/spec/vcr/Bigrig_DockerAdapter/_push/should_push_the_image.yml +482 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_does_not_exist/raises_a_ContainerNotFoundError.yml +227 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_exists/should_remove_the_container.yml +222 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_is_running/raises_a_ContainerRunningError.yml +80 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_doesnt_exist/raises_an_error.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_exists/removes_the_image.yml +199 -0
- data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name/starts_the_container_with_the_right_name.yml +204 -0
- data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name_and_env_variables/starts_the_container_with_env_set.yml +204 -0
- data/spec/vcr/Bigrig_DockerAdapter/_run/given_an_image_id_that_exists/and_a_name_and_ports/starts_the_container_with_ports_exposed.yml +230 -0
- data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_does_not_exist/returns_false.yml +115 -0
- data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_not_running/returns_false.yml +82 -0
- data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_running/returns_true.yml +103 -0
- data/spec/vcr/Bigrig_DockerAdapter/_tag/should_tag_the_image.yml +290 -0
- data/spec/vcr/Bigrig_LogAction/_perform/follows_the_log.yml +163 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_a_path/builds_the_image_before_starting_it.yml +310 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_ip/should_pass_hosts_to_container.yml +430 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_name/should_lookup_ips_for_hosts_with_a_hostname.yml +430 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_links/should_pass_links_to_the_right_container.yml +805 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/launches_both_containers_in_parallel.yml +731 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/spins_up_multiple_containers.yml +805 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/should_spin_up_a_single_container.yml +404 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/when_a_dead_container_exists/should_remove_existing_containers.yml +329 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/should_pass_volumes_from_to_the_right_container.yml +805 -0
- data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/starts_the_dependant_container_last.yml +731 -0
- data/spec/vcr/Bigrig_RunAction/_perform/when_activating_profiles_that_do_not_exist/ignores_the_missing_profile.yml +409 -0
- data/spec/vcr/Bigrig_RunAction/_perform/with_a_file_with_active_profiles/uses_the_overridden_image.yml +432 -0
- data/spec/vcr/bigrig/destroy/spec/data/single_json/kills_the_container.yml +166 -0
- data/spec/vcr/bigrig/dev/spec/data/dev_json/activates_the_dev_profile.yml +37 -0
- data/spec/vcr/bigrig/dev/spec/data/dev_json/destroys_containers_on_exit.yml +227 -0
- data/spec/vcr/bigrig/dev/spec/data/dev_json/starts_the_containers.yml +107 -0
- data/spec/vcr/bigrig/logs/spec/data/log_json/tails_the_logs.yml +163 -0
- data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/leaves_existing_env_values_alone.yml +110 -0
- data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_env.yml +110 -0
- data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_tag.yml +103 -0
- data/spec/vcr/bigrig/run/spec/data/single_json/sends_the_name_of_the_container_to_stdout.yml +75 -0
- data/spec/vcr/bigrig/run/spec/data/single_json/starts_the_container.yml +101 -0
- data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/-c/cleans_the_image_when_its_done.yml +212 -0
- data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/builds_and_pushes_the_image.yml +128 -0
- data/spec/vcr/bigrig_bin_bigrig_destroy_spec/data/single_json_kills_the_container.yml +179 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_activates_the_dev_profile.yml +1068 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_destroys_containers_on_exit.yml +1070 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_starts_the_containers.yml +1068 -0
- data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_tails_the_logs.yml +1075 -0
- data/spec/vcr/bigrig_bin_bigrig_logs_spec/data/log_json_tails_the_logs.yml +69 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_leaves_existing_env_values_alone.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_env.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_tag.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_sends_the_name_of_the_container_to_stdout.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_starts_the_container.yml +306 -0
- data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_-c_cleans_the_image_when_its_done.yml +335 -0
- data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_builds_and_pushes_the_image.yml +285 -0
- data/test/dev/shipper.json +24 -0
- data/test/logs/bigrig.json +6 -0
- data/test/logs/container1/Dockerfile +4 -0
- data/test/logs/container1/run.sh +7 -0
- data/test/logs/container2/Dockerfile +4 -0
- data/test/logs/container2/run.sh +7 -0
- data/test/ship/bigrig-1.2.3.json +1 -0
- data/test/ship/bigrig.json +5 -0
- data/test/ship/registry.json +11 -0
- data/test/ship/ship/Dockerfile +4 -0
- data/test/ship/ship/run.sh +6 -0
- data/test/volumes_from/exports_volumes/Dockerfile +8 -0
- data/test/volumes_from/exports_volumes/index.html +8 -0
- data/test/volumes_from/exports_volumes/run.sh +7 -0
- data/test/volumes_from/shipper.json +14 -0
- metadata +278 -8
@@ -0,0 +1,409 @@
|
|
1
|
+
require 'pry'
|
2
|
+
module Bigrig
|
3
|
+
describe DockerAdapter do
|
4
|
+
|
5
|
+
describe '::build' do
|
6
|
+
subject { described_class.build path }
|
7
|
+
let(:path) { test_file 'build' }
|
8
|
+
let(:image_id) { subject }
|
9
|
+
|
10
|
+
it 'builds the given directory', :vcr do
|
11
|
+
expect { Docker::Image.get(image_id) }.to_not raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'passes build input to a block', :vcr do
|
15
|
+
described_class.build path do |chunk|
|
16
|
+
puts "chunk: #{chunk}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '::container_exists?' do
|
22
|
+
subject { described_class.container_exists?(name) }
|
23
|
+
|
24
|
+
context 'when the container exists' do
|
25
|
+
let!(:container) { Docker::Container.create 'name' => name, 'Image' => 'hawknewton/true' }
|
26
|
+
let(:name) { 'container_exists' }
|
27
|
+
|
28
|
+
after { container.remove }
|
29
|
+
|
30
|
+
it 'is true', :vcr do
|
31
|
+
is_expected.to be true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when the container does not exist' do
|
36
|
+
let(:name) { 'container_does_not_exist' }
|
37
|
+
|
38
|
+
it 'is false', :vcr do
|
39
|
+
is_expected.to be false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '::image_id_by_tag' do
|
45
|
+
subject { DockerAdapter.image_id_by_tag(tag) }
|
46
|
+
|
47
|
+
context 'when the image exists' do
|
48
|
+
let(:tag) { 'image_exists' }
|
49
|
+
let!(:image) { Docker::Image.create 'fromImage' => 'hawknewton/true' }
|
50
|
+
|
51
|
+
# Get the long id
|
52
|
+
let(:image_id) { Docker::Image.get(image.id).id }
|
53
|
+
|
54
|
+
before { image.tag 'repo' => tag }
|
55
|
+
after { image.remove name: tag }
|
56
|
+
|
57
|
+
it 'returns the image id', :vcr do
|
58
|
+
is_expected.to eq image_id
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when the image does not exist' do
|
63
|
+
let(:tag) { 'hawknewton/true:not_exist' }
|
64
|
+
|
65
|
+
before do
|
66
|
+
begin
|
67
|
+
image = Docker::Image.get tag
|
68
|
+
image.remove name: tag
|
69
|
+
rescue Docker::Error::NotFoundError # rubocop:disable Lint/HandleExceptions
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'raise a ImageNotFoundError', :vcr do
|
74
|
+
expect { subject }.to raise_error ImageNotFoundError
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '::kill' do
|
80
|
+
subject { described_class.kill container_id }
|
81
|
+
|
82
|
+
context 'given the container is running' do
|
83
|
+
let(:container) { Docker::Container.create('Image' => image.id).tap(&:start) }
|
84
|
+
let(:container_id) { container.id }
|
85
|
+
let(:image) { Docker::Image.create 'fromImage' => 'hawknewton/show-env' }
|
86
|
+
let(:running?) { container.json['State']['Running'] }
|
87
|
+
let!(:kill) { subject }
|
88
|
+
|
89
|
+
after { container.delete force: true }
|
90
|
+
|
91
|
+
it 'should kill the container', :vcr do
|
92
|
+
expect(running?).to be false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'given the container does not exist' do
|
97
|
+
let(:container_id) { 'doesnotexist' }
|
98
|
+
|
99
|
+
it 'should raise an error', :vcr do
|
100
|
+
expect { subject }.to raise_error ContainerNotFoundError
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '::logs' do
|
106
|
+
subject { described_class.logs(container.id) { |s, c| logs << [s, c] } }
|
107
|
+
let(:logs) { [] }
|
108
|
+
let(:image) { Docker::Image.create 'fromImage' => 'hawknewton/log-test:0.0.1' }
|
109
|
+
let(:container) { Docker::Container.get 'log-test' }
|
110
|
+
|
111
|
+
before do
|
112
|
+
Docker::Container.create('Image' => image.id, 'name' => 'log-test').start
|
113
|
+
end
|
114
|
+
|
115
|
+
after { container.kill.delete }
|
116
|
+
|
117
|
+
it 'streams logs to a block', :vcr do
|
118
|
+
allow(Docker::Container).to receive(:get).with(container.id, {}, anything).
|
119
|
+
and_return container
|
120
|
+
expect(container).to receive(:streaming_logs).
|
121
|
+
with(follow: true, stdout: true, stderr: true).
|
122
|
+
and_yield('stream #1', 'I am log message #1').
|
123
|
+
and_yield('stream #2', 'I am log message #2')
|
124
|
+
|
125
|
+
subject
|
126
|
+
expect(logs).to include ['stream #1', 'I am log message #1']
|
127
|
+
expect(logs).to include ['stream #2', 'I am log message #2']
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '::push' do
|
132
|
+
subject { described_class.push tag, credentials }
|
133
|
+
let(:credentials) { nil }
|
134
|
+
let(:host) { URI.parse(Docker.connection.url).host }
|
135
|
+
let(:image) { Docker::Image.import test_file 'tiny-image.tar' }
|
136
|
+
let(:repo) { "#{host}:5000/test/push-me" }
|
137
|
+
let(:version) { '1.2.3' }
|
138
|
+
let(:tag) { "#{repo}:#{version}" }
|
139
|
+
let(:registry) do
|
140
|
+
Docker::Image.create 'fromImage' => 'registry:latest'
|
141
|
+
Docker::Container.create(
|
142
|
+
'name' => 'registry',
|
143
|
+
'Image' => 'registry',
|
144
|
+
'Env' => ['GUNICORN_OPTS=[--preload]'],
|
145
|
+
'ExposedPorts' => {
|
146
|
+
'5000/tcp' => {}
|
147
|
+
},
|
148
|
+
'HostConfig' => {
|
149
|
+
'PortBindings' => { '5000/tcp' => [{ 'HostPort' => '5000' }] }
|
150
|
+
}
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
before do
|
155
|
+
registry.start
|
156
|
+
image.tag 'repo' => repo, 'tag' => version
|
157
|
+
sleep 1
|
158
|
+
end
|
159
|
+
|
160
|
+
after do
|
161
|
+
registry.kill.delete
|
162
|
+
image.remove 'force' => true
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should push the image', :vcr do
|
166
|
+
subject
|
167
|
+
Docker::Image.get(tag).remove 'force' => true
|
168
|
+
expect { Docker::Image.create 'fromImage' => tag }.to_not raise_error
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'given credentials' do
|
172
|
+
let(:credentials) do
|
173
|
+
{
|
174
|
+
'username' => 'username',
|
175
|
+
'password' => 'pass',
|
176
|
+
'email' => 'email@email.com',
|
177
|
+
'serveraddress' => 'https://index.docker.io/v1'
|
178
|
+
}
|
179
|
+
end
|
180
|
+
# I don't have the energy to set one of these up for real
|
181
|
+
|
182
|
+
it 'will pass login and password', :vcr do
|
183
|
+
creds = {
|
184
|
+
'username' => 'username',
|
185
|
+
'password' => 'pass',
|
186
|
+
'serveraddress' => 'https://index.docker.io/v1',
|
187
|
+
'email' => 'email@email.com'
|
188
|
+
}
|
189
|
+
expect_any_instance_of(Docker::Image).to receive(:push).
|
190
|
+
with(creds, {})
|
191
|
+
subject
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe '::pull' do
|
197
|
+
subject { described_class.pull repo }
|
198
|
+
|
199
|
+
context 'given the repo exists' do
|
200
|
+
let(:repo) { 'hawknewton/true:latest' }
|
201
|
+
let(:image_id) { Docker::Image.get(repo).id }
|
202
|
+
|
203
|
+
it 'returns the image id', :vcr do
|
204
|
+
is_expected.to eq image_id
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'given the repo does not exist' do
|
209
|
+
let(:repo) { 'hawknewton/doesnotexist' }
|
210
|
+
|
211
|
+
it 'raises a RepoNotFoundError', :vcr do
|
212
|
+
expect { subject }.to raise_error RepoNotFoundError
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'given a block to capture output' do
|
217
|
+
let(:repo) { 'hawknewton/true' }
|
218
|
+
let(:output) { '' }
|
219
|
+
let(:block) { proc { |chunk| output << chunk } }
|
220
|
+
let!(:image_id) { described_class.pull repo, &block }
|
221
|
+
|
222
|
+
it 'should capture output', :vcr do
|
223
|
+
expect(output).to match(/Pulling repository hawknewton\/true/)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe '::remove_container' do
|
229
|
+
subject { described_class.remove_container container_id }
|
230
|
+
|
231
|
+
context 'when the container exists' do
|
232
|
+
let(:container_id) { Docker::Container.create('Image' => 'hawknewton/true').id }
|
233
|
+
let!(:remove) { subject }
|
234
|
+
|
235
|
+
it 'should remove the container', :vcr do
|
236
|
+
expect { Docker::Container.get container_id }.
|
237
|
+
to raise_error { Docker::Error::NotFoundError }
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context 'when the container does not exist' do
|
242
|
+
let(:container_id) { 'doesnotexist' }
|
243
|
+
|
244
|
+
it 'raises a ContainerNotFoundError', :vcr do
|
245
|
+
expect { subject }.to raise_error ContainerNotFoundError
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'when the container is running' do
|
250
|
+
let(:container_id) do
|
251
|
+
Docker::Container.create('Image' => 'hawknewton/true').tap(&:start).id
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'raises a ContainerRunningError', :vcr do
|
255
|
+
expect { subject }.to raise_error ContainerRunningError
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe '::remove_image' do
|
261
|
+
subject { described_class.remove_image image_id }
|
262
|
+
|
263
|
+
context 'when the image exists' do
|
264
|
+
let(:image_id) { Docker::Image.import(test_file 'tiny-image.tar').id }
|
265
|
+
|
266
|
+
it 'removes the image', :vcr do
|
267
|
+
subject
|
268
|
+
expect { Docker::Image.get(image_id) }.to raise_error(/404 Not Found/)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
context 'when the image doesn''t exist' do
|
273
|
+
let(:image_id) { 'does_not_exist' }
|
274
|
+
|
275
|
+
it 'raises an error', :vcr do
|
276
|
+
expect { subject }.to raise_error ImageNotFoundError
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe '::run' do
|
282
|
+
subject { DockerAdapter.run({ image_id: image_id }.merge opts) }
|
283
|
+
|
284
|
+
context 'given an image_id that exists' do
|
285
|
+
let(:image_id) { 'hawknewton/show-env' }
|
286
|
+
let(:container_id) { subject }
|
287
|
+
let(:container) { Docker::Container.get container_id }
|
288
|
+
let(:running?) { container.json['State']['Running'] }
|
289
|
+
|
290
|
+
context 'and a name' do
|
291
|
+
let(:opts) { { name: name } }
|
292
|
+
let(:name) { 'and_a_name' }
|
293
|
+
|
294
|
+
# strip the leading '/' from the json name
|
295
|
+
let(:container_name) { container.json['Name'][1..-1] }
|
296
|
+
|
297
|
+
after { Docker::Container.get(name).kill!.delete }
|
298
|
+
|
299
|
+
it 'starts the container with the right name', :vcr do
|
300
|
+
expect(running?).to be true
|
301
|
+
expect(container_name).to eq name
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'and a name and ports' do
|
306
|
+
let(:free_port) { TCPServer.new('0.0.0.0', 0).addr[1] }
|
307
|
+
let(:opts) { { name: name, ports: ["#{free_port}:80", '70'] } }
|
308
|
+
let(:name) { 'with_ports' }
|
309
|
+
let(:exposed_ports) { container.json['Config']['ExposedPorts'].map { |k, _| k } }
|
310
|
+
let(:mapped_ports) do
|
311
|
+
container.json['NetworkSettings']['Ports'].each_with_object({}) do |arr, hash|
|
312
|
+
hash[arr[0]] = arr[1][0]['HostPort']
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
after { Docker::Container.get(name).kill!.delete }
|
317
|
+
|
318
|
+
it 'starts the container with ports exposed', :vcr do
|
319
|
+
expect(running?).to be true
|
320
|
+
expect(exposed_ports).to eq ['70/tcp', '80/tcp']
|
321
|
+
expect(mapped_ports).to include '80/tcp'
|
322
|
+
expect(mapped_ports).to include '70/tcp'
|
323
|
+
|
324
|
+
# Because the free port will move around under VCR we can't actually
|
325
|
+
# assert anything
|
326
|
+
expect(mapped_ports['80/tcp'].to_i).to be_a Fixnum
|
327
|
+
expect(mapped_ports['70/tcp'].to_i).to be_a Fixnum
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
context 'and a name and env variables' do
|
332
|
+
let(:name) { 'with_env' }
|
333
|
+
let(:opts) { { name: name, env: { 'NAME1' => 'VALUE1', 'NAME2' => 'VALUE2' } } }
|
334
|
+
let(:envs) { container.json['Config']['Env'] }
|
335
|
+
|
336
|
+
after { Docker::Container.get(name).kill!.delete }
|
337
|
+
|
338
|
+
it 'starts the container with env set', :vcr do
|
339
|
+
expect(running?).to be true
|
340
|
+
expect(envs).to include 'NAME1=VALUE1'
|
341
|
+
expect(envs).to include 'NAME2=VALUE2'
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe '::running?' do
|
348
|
+
subject { described_class.running? container_id }
|
349
|
+
let(:result) { subject }
|
350
|
+
|
351
|
+
context 'when the container is running' do
|
352
|
+
let(:container) do
|
353
|
+
Docker::Container.create('Image' => 'hawknewton/show-env')
|
354
|
+
end
|
355
|
+
let(:container_id) { container.id }
|
356
|
+
|
357
|
+
before { container.start }
|
358
|
+
after { container.delete force: true }
|
359
|
+
|
360
|
+
it 'returns true', :vcr do
|
361
|
+
expect(result).to be true
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
context 'when the container is not running' do
|
366
|
+
let(:container) do
|
367
|
+
Docker::Container.create('Image' => 'hawknewton/show-env')
|
368
|
+
end
|
369
|
+
let(:container_id) { container.id }
|
370
|
+
|
371
|
+
after { container.delete force: true }
|
372
|
+
|
373
|
+
it 'returns false', :vcr do
|
374
|
+
expect(result).to be false
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
context 'when the container does not exist', :vcr do
|
379
|
+
let(:container_id) { 'doesnotexist' }
|
380
|
+
|
381
|
+
it 'returns false' do
|
382
|
+
expect(result).to be false
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
describe '::tag' do
|
388
|
+
subject { described_class.tag id, tag }
|
389
|
+
let(:id) { Docker::Image.create('fromImage' => 'hawknewton/true').id }
|
390
|
+
let(:tag) { 'test/tag:1.2.3' }
|
391
|
+
|
392
|
+
before do
|
393
|
+
begin
|
394
|
+
Docker::Image.get(tag).remove 'force' => true
|
395
|
+
rescue Docker::Error::NotFoundError # rubocop:disable Lint/HandleExceptions
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
after do
|
400
|
+
Docker::Image.get(tag).remove 'force' => true
|
401
|
+
end
|
402
|
+
|
403
|
+
it 'should tag the image', :vcr do
|
404
|
+
subject
|
405
|
+
expect { Docker::Image.get 'test/tag:1.2.3' }.to_not raise_error
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Bigrig
|
2
|
+
describe Application do
|
3
|
+
it { is_expected.to respond_to :name }
|
4
|
+
it { is_expected.to respond_to :name= }
|
5
|
+
|
6
|
+
it { is_expected.to respond_to :containers }
|
7
|
+
it { is_expected.to respond_to :containers= }
|
8
|
+
|
9
|
+
describe '::from_json' do
|
10
|
+
subject { described_class.from_json json }
|
11
|
+
|
12
|
+
context 'given a simple file' do
|
13
|
+
let(:file) { 'single.json' }
|
14
|
+
let(:json) { Descriptor.read(test_file(file)).as_json }
|
15
|
+
|
16
|
+
its('containers.size') { is_expected.to eq 1 }
|
17
|
+
|
18
|
+
it 'should set the container name' do
|
19
|
+
expect(subject.containers[0].name).to eq 'single-test'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should set the container tag' do
|
23
|
+
expect(subject.containers[0].tag).to eq '0.0.1'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should set the container repo' do
|
27
|
+
expect(subject.containers[0].repo).to eq 'hawknewton/show-env'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Bigrig
|
2
|
+
describe Container do
|
3
|
+
it { is_expected.to respond_to :hosts= }
|
4
|
+
it { is_expected.to respond_to :hosts }
|
5
|
+
it { is_expected.to respond_to :links= }
|
6
|
+
it { is_expected.to respond_to :links }
|
7
|
+
it { is_expected.to respond_to :name }
|
8
|
+
it { is_expected.to respond_to :name= }
|
9
|
+
it { is_expected.to respond_to :path }
|
10
|
+
it { is_expected.to respond_to :path= }
|
11
|
+
it { is_expected.to respond_to :ports }
|
12
|
+
it { is_expected.to respond_to :ports= }
|
13
|
+
it { is_expected.to respond_to :repo }
|
14
|
+
it { is_expected.to respond_to :repo= }
|
15
|
+
it { is_expected.to respond_to :tag }
|
16
|
+
it { is_expected.to respond_to :tag= }
|
17
|
+
it { is_expected.to respond_to :volumes_from }
|
18
|
+
it { is_expected.to respond_to :volumes_from= }
|
19
|
+
|
20
|
+
it 'accepts volumes_from as an array' do
|
21
|
+
expect(Container.from_json(nil, 'volumes_from' => ['test']).volumes_from).to be_kind_of Array
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'wraps a single volumes_from in an array' do
|
25
|
+
expect(Container.from_json(nil, 'volumes_from' => 'test').volumes_from).to be_kind_of Array
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'accepts links as an array' do
|
29
|
+
expect(Container.from_json(nil, 'links' => ['machine:alias']).links).to be_kind_of Array
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'wraps a single links in an array' do
|
33
|
+
expect(Container.from_json(nil, 'links' => 'machine:alias').links).to be_kind_of Array
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'accepts hosts as an array' do
|
37
|
+
expect(Container.from_json(nil, 'links' => ['host:alias']).links).to be_kind_of Array
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'wraps a single links in an array' do
|
41
|
+
expect(Container.from_json(nil, 'hosts' => 'host:alias').links).to be_kind_of Array
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'depends on containers it mounts volumes from' do
|
45
|
+
expect(Container.from_json(nil, 'volumes_from' => 'test').dependencies).to eq ['test']
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'depends on containers it links to' do
|
49
|
+
expect(Container.from_json(nil, 'links' => 'machine:alias').dependencies).to eq ['machine']
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#dependencies' do
|
53
|
+
it 'returns an empty array when no dependencies are present' do
|
54
|
+
expect(Container.from_json(nil, {}).dependencies).to eq []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '::from_json' do
|
59
|
+
subject { Container.from_json name, json }
|
60
|
+
let(:name) { 'name' }
|
61
|
+
|
62
|
+
context 'with an empty json document' do
|
63
|
+
let(:json) { {} }
|
64
|
+
|
65
|
+
its(:env) { is_expected.to be_empty }
|
66
|
+
its(:env) { is_expected.to be_a Hash }
|
67
|
+
|
68
|
+
its(:volumes_from) { is_expected.to be_empty }
|
69
|
+
its(:volumes_from) { is_expected.to be_a Array }
|
70
|
+
|
71
|
+
its(:ports) { is_expected.to be_a Array }
|
72
|
+
its(:ports) { is_expected.to be_empty }
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'given json with a path' do
|
77
|
+
let(:json) { { 'path' => '/path/to/Dockerfile' } }
|
78
|
+
|
79
|
+
it 'sets the path' do
|
80
|
+
expect(subject.path).to eq '/path/to/Dockerfile'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'given json with env params' do
|
85
|
+
let(:json) { { 'env' => { 'NAME1' => 'VALUE1', 'NAME2' => 'VALUE2' } } }
|
86
|
+
|
87
|
+
it 'parses the environment variables' do
|
88
|
+
expect(subject.env).to include(
|
89
|
+
'NAME1' => 'VALUE1',
|
90
|
+
'NAME2' => 'VALUE2'
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'that reference environment variables' do
|
95
|
+
let(:json) { { 'env' => { 'NAME1' => "\#{ENV['NAME1']}" } } }
|
96
|
+
|
97
|
+
before { ENV['NAME1'] = 'ENV1' }
|
98
|
+
after { ENV['NAME1'] = nil }
|
99
|
+
|
100
|
+
it 'retrieves the variable from the environemnt' do
|
101
|
+
expect(subject.env).to include('NAME1' => 'ENV1')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'given json with ports' do
|
107
|
+
let(:json) { { 'ports' => ['80:8080', '12345'] } }
|
108
|
+
|
109
|
+
it 'has ports' do
|
110
|
+
expect(subject.ports).to eq ['80:8080', '12345']
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Bigrig
|
2
|
+
describe OutputParser do
|
3
|
+
describe '#parse' do
|
4
|
+
subject do
|
5
|
+
parser = described_class.new
|
6
|
+
[input].flatten.map { |i| parser.parse i }.last
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:mock_io) { double IO }
|
10
|
+
let(:output) { subject }
|
11
|
+
|
12
|
+
context 'with input that has a stream' do
|
13
|
+
let(:input) { '{"stream": "value"}' }
|
14
|
+
|
15
|
+
it 'returns the value of stream' do
|
16
|
+
expect(output).to eq 'value'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'with input that has no id and a status' do
|
21
|
+
let(:input) { '{"status": "status_value"}' }
|
22
|
+
|
23
|
+
it 'returns the value of status with a linefeed' do
|
24
|
+
expect(output).to eq "status_value\n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'with input that has an id, status, and progress' do
|
29
|
+
let(:input) { '{"id": "id", "status": "status", "progress" : "progress" }' }
|
30
|
+
|
31
|
+
it 'formats the output with a trailing carrage return' do
|
32
|
+
expect(output).to eq "id: status progress\r"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'when the id changes' do
|
37
|
+
let(:input) do
|
38
|
+
['{"id": "id", "status": "status", "progress" : "progress" }',
|
39
|
+
'{"id": "new_id", "status": "status", "progress" : "progress" }']
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'prints a linefeed' do
|
43
|
+
expect(output).to eq "\nnew_id: status progress\r"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when the id noralizes after changing' do
|
48
|
+
let(:input) do
|
49
|
+
['{"id": "id", "status": "status", "progress" : "progress" }',
|
50
|
+
'{"id": "new_id", "status": "status", "progress" : "progress" }',
|
51
|
+
'{"id": "new_id", "status": "status", "progress" : "progress" }']
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'does not print a linefeed' do
|
55
|
+
expect(output).to eq "new_id: status progress\r"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'when a shorter message is returned with the same id' do
|
60
|
+
let(:input) do
|
61
|
+
['{"id": "id", "status": "status", "progress" : "i am a much longer message" }',
|
62
|
+
'{"id": "id", "status": "status", "progress" : "progress" }']
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'pads the output' do
|
66
|
+
expect(output).to eq "id: status progress \r"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|