bigrig 0.0.0 → 0.0.7

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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +20 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.md +19 -0
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +84 -0
  9. data/LICENSE +28 -0
  10. data/README.md +191 -0
  11. data/Rakefile +10 -0
  12. data/bigrig.gemspec +29 -0
  13. data/bin/bigrig +113 -0
  14. data/lib/bigrig/actions/destroy_action.rb +21 -0
  15. data/lib/bigrig/actions/dev_action.rb +19 -0
  16. data/lib/bigrig/actions/log_action.rb +45 -0
  17. data/lib/bigrig/actions/run_action.rb +14 -0
  18. data/lib/bigrig/actions/ship_action.rb +51 -0
  19. data/lib/bigrig/actions.rb +5 -0
  20. data/lib/bigrig/dependency_graph.rb +21 -0
  21. data/lib/bigrig/descriptor.rb +43 -0
  22. data/lib/bigrig/docker_adapter.rb +129 -0
  23. data/lib/bigrig/models/application.rb +22 -0
  24. data/lib/bigrig/models/base_model.rb +7 -0
  25. data/lib/bigrig/models/container.rb +40 -0
  26. data/lib/bigrig/models.rb +3 -0
  27. data/lib/bigrig/output_parser.rb +52 -0
  28. data/lib/bigrig/runner.rb +94 -0
  29. data/lib/bigrig/version.rb +3 -0
  30. data/lib/bigrig.rb +8 -0
  31. data/spec/bigrig/actions/destroy_action_spec.rb +55 -0
  32. data/spec/bigrig/actions/dev_action_spec.rb +17 -0
  33. data/spec/bigrig/actions/log_action_spec.rb +33 -0
  34. data/spec/bigrig/actions/run_action_spec.rb +270 -0
  35. data/spec/bigrig/actions/ship_action_spec.rb +82 -0
  36. data/spec/bigrig/dependency_graph_spec.rb +19 -0
  37. data/spec/bigrig/descriptor_spec.rb +56 -0
  38. data/spec/bigrig/docker_adapter_spec.rb +409 -0
  39. data/spec/bigrig/models/application_spec.rb +32 -0
  40. data/spec/bigrig/models/container_spec.rb +115 -0
  41. data/spec/bigrig/output_parser_spec.rb +71 -0
  42. data/spec/bigrig_spec.rb +249 -0
  43. data/spec/data/addscontainer.json +24 -0
  44. data/spec/data/build/Dockerfile +2 -0
  45. data/spec/data/build/test +0 -0
  46. data/spec/data/dev.json +26 -0
  47. data/spec/data/duplicate.json +13 -0
  48. data/spec/data/env.json +12 -0
  49. data/spec/data/hosts_ip.json +12 -0
  50. data/spec/data/hosts_name.json +11 -0
  51. data/spec/data/links.json +13 -0
  52. data/spec/data/log.json +8 -0
  53. data/spec/data/multiple.json +13 -0
  54. data/spec/data/path.json +5 -0
  55. data/spec/data/ports.json +9 -0
  56. data/spec/data/profiles.json +24 -0
  57. data/spec/data/ship.json +5 -0
  58. data/spec/data/single.json +8 -0
  59. data/spec/data/tagandpath.json +24 -0
  60. data/spec/data/tiny-image.tar +0 -0
  61. data/spec/data/volumes.json +13 -0
  62. data/spec/spec_helper.rb +104 -0
  63. data/spec/support/bigrig_vcr +8 -0
  64. data/spec/support/vcr.rb +15 -0
  65. data/spec/vcr/Bigrig_DestroyAction/_perform/given_json_with_a_single_container/and_the_container_has_exited/should_remove_the_container.yml +392 -0
  66. 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
  67. data/spec/vcr/Bigrig_DockerAdapter/_build/builds_the_given_directory.yml +63 -0
  68. data/spec/vcr/Bigrig_DockerAdapter/_build/passes_build_input_to_a_block.yml +35 -0
  69. data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_does_not_exist/is_false.yml +115 -0
  70. data/spec/vcr/Bigrig_DockerAdapter/_container_exists_/when_the_container_exists/is_true.yml +82 -0
  71. data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_does_not_exist/raise_a_ImageNotFoundError.yml +172 -0
  72. data/spec/vcr/Bigrig_DockerAdapter/_image_id_by_tag/when_the_image_exists/returns_the_image_id.yml +148 -0
  73. data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_does_not_exist/should_raise_an_error.yml +115 -0
  74. data/spec/vcr/Bigrig_DockerAdapter/_kill/given_the_container_is_running/should_kill_the_container.yml +200 -0
  75. data/spec/vcr/Bigrig_DockerAdapter/_logs/streams_logs_to_a_block.yml +163 -0
  76. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_a_block_to_capture_output/should_capture_output.yml +64 -0
  77. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_does_not_exist/raises_a_RepoNotFoundError.yml +30 -0
  78. data/spec/vcr/Bigrig_DockerAdapter/_pull/given_the_repo_exists/returns_the_image_id.yml +92 -0
  79. data/spec/vcr/Bigrig_DockerAdapter/_push/given_credentials/will_pass_login_and_password.yml +254 -0
  80. data/spec/vcr/Bigrig_DockerAdapter/_push/should_push_the_image.yml +482 -0
  81. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_does_not_exist/raises_a_ContainerNotFoundError.yml +227 -0
  82. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_exists/should_remove_the_container.yml +222 -0
  83. data/spec/vcr/Bigrig_DockerAdapter/_remove_container/when_the_container_is_running/raises_a_ContainerRunningError.yml +80 -0
  84. data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_doesnt_exist/raises_an_error.yml +115 -0
  85. data/spec/vcr/Bigrig_DockerAdapter/_remove_image/when_the_image_exists/removes_the_image.yml +199 -0
  86. 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
  87. 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
  88. 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
  89. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_does_not_exist/returns_false.yml +115 -0
  90. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_not_running/returns_false.yml +82 -0
  91. data/spec/vcr/Bigrig_DockerAdapter/_running_/when_the_container_is_running/returns_true.yml +103 -0
  92. data/spec/vcr/Bigrig_DockerAdapter/_tag/should_tag_the_image.yml +290 -0
  93. data/spec/vcr/Bigrig_LogAction/_perform/follows_the_log.yml +163 -0
  94. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_a_path/builds_the_image_before_starting_it.yml +310 -0
  95. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_ip/should_pass_hosts_to_container.yml +430 -0
  96. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_hosts_by_name/should_lookup_ips_for_hosts_with_a_hostname.yml +430 -0
  97. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_links/should_pass_links_to_the_right_container.yml +805 -0
  98. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/launches_both_containers_in_parallel.yml +731 -0
  99. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_multiple_containers/spins_up_multiple_containers.yml +805 -0
  100. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/should_spin_up_a_single_container.yml +404 -0
  101. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_one_container/when_a_dead_container_exists/should_remove_existing_containers.yml +329 -0
  102. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/should_pass_volumes_from_to_the_right_container.yml +805 -0
  103. data/spec/vcr/Bigrig_RunAction/_perform/given_a_file_with_volumes_from/starts_the_dependant_container_last.yml +731 -0
  104. data/spec/vcr/Bigrig_RunAction/_perform/when_activating_profiles_that_do_not_exist/ignores_the_missing_profile.yml +409 -0
  105. data/spec/vcr/Bigrig_RunAction/_perform/with_a_file_with_active_profiles/uses_the_overridden_image.yml +432 -0
  106. data/spec/vcr/bigrig/destroy/spec/data/single_json/kills_the_container.yml +166 -0
  107. data/spec/vcr/bigrig/dev/spec/data/dev_json/activates_the_dev_profile.yml +37 -0
  108. data/spec/vcr/bigrig/dev/spec/data/dev_json/destroys_containers_on_exit.yml +227 -0
  109. data/spec/vcr/bigrig/dev/spec/data/dev_json/starts_the_containers.yml +107 -0
  110. data/spec/vcr/bigrig/logs/spec/data/log_json/tails_the_logs.yml +163 -0
  111. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/leaves_existing_env_values_alone.yml +110 -0
  112. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_env.yml +110 -0
  113. data/spec/vcr/bigrig/run/spec/data/profiles_json_-p_qa/overrides_the_tag.yml +103 -0
  114. data/spec/vcr/bigrig/run/spec/data/single_json/sends_the_name_of_the_container_to_stdout.yml +75 -0
  115. data/spec/vcr/bigrig/run/spec/data/single_json/starts_the_container.yml +101 -0
  116. data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/-c/cleans_the_image_when_its_done.yml +212 -0
  117. data/spec/vcr/bigrig/ship/spec/data/ship_json/with_a_version/builds_and_pushes_the_image.yml +128 -0
  118. data/spec/vcr/bigrig_bin_bigrig_destroy_spec/data/single_json_kills_the_container.yml +179 -0
  119. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_activates_the_dev_profile.yml +1068 -0
  120. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_destroys_containers_on_exit.yml +1070 -0
  121. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_starts_the_containers.yml +1068 -0
  122. data/spec/vcr/bigrig_bin_bigrig_dev_spec/data/dev_json_tails_the_logs.yml +1075 -0
  123. data/spec/vcr/bigrig_bin_bigrig_logs_spec/data/log_json_tails_the_logs.yml +69 -0
  124. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_leaves_existing_env_values_alone.yml +306 -0
  125. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_env.yml +306 -0
  126. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/profiles_json_-p_qa_overrides_the_tag.yml +306 -0
  127. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_sends_the_name_of_the_container_to_stdout.yml +306 -0
  128. data/spec/vcr/bigrig_bin_bigrig_run_spec/data/single_json_starts_the_container.yml +306 -0
  129. data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_-c_cleans_the_image_when_its_done.yml +335 -0
  130. data/spec/vcr/bigrig_bin_bigrig_ship_spec/data/ship_json_with_a_version_builds_and_pushes_the_image.yml +285 -0
  131. data/test/dev/shipper.json +24 -0
  132. data/test/logs/bigrig.json +6 -0
  133. data/test/logs/container1/Dockerfile +4 -0
  134. data/test/logs/container1/run.sh +7 -0
  135. data/test/logs/container2/Dockerfile +4 -0
  136. data/test/logs/container2/run.sh +7 -0
  137. data/test/ship/bigrig-1.2.3.json +1 -0
  138. data/test/ship/bigrig.json +5 -0
  139. data/test/ship/registry.json +11 -0
  140. data/test/ship/ship/Dockerfile +4 -0
  141. data/test/ship/ship/run.sh +6 -0
  142. data/test/volumes_from/exports_volumes/Dockerfile +8 -0
  143. data/test/volumes_from/exports_volumes/index.html +8 -0
  144. data/test/volumes_from/exports_volumes/run.sh +7 -0
  145. data/test/volumes_from/shipper.json +14 -0
  146. 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