knife-container 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +4 -0
  5. data/CONTRIBUTING.md +152 -0
  6. data/Gemfile +10 -0
  7. data/LICENSE +201 -0
  8. data/README.md +59 -0
  9. data/Rakefile +16 -0
  10. data/knife-container.gemspec +31 -0
  11. data/lib/chef/knife/container_docker_build.rb +243 -0
  12. data/lib/chef/knife/container_docker_init.rb +262 -0
  13. data/lib/knife-container/chef_runner.rb +83 -0
  14. data/lib/knife-container/command.rb +45 -0
  15. data/lib/knife-container/generator.rb +88 -0
  16. data/lib/knife-container/helpers.rb +16 -0
  17. data/lib/knife-container/skeletons/knife_container/files/default/plugins/docker_container.rb +37 -0
  18. data/lib/knife-container/skeletons/knife_container/metadata.rb +7 -0
  19. data/lib/knife-container/skeletons/knife_container/recipes/docker_init.rb +181 -0
  20. data/lib/knife-container/skeletons/knife_container/templates/default/berksfile.erb +5 -0
  21. data/lib/knife-container/skeletons/knife_container/templates/default/config.rb.erb +16 -0
  22. data/lib/knife-container/skeletons/knife_container/templates/default/dockerfile.erb +9 -0
  23. data/lib/knife-container/skeletons/knife_container/templates/default/dockerignore.erb +0 -0
  24. data/lib/knife-container/skeletons/knife_container/templates/default/node_name.erb +1 -0
  25. data/lib/knife-container/version.rb +5 -0
  26. data/spec/functional/docker_container_ohai_spec.rb +20 -0
  27. data/spec/functional/fixtures/ohai/Dockerfile +3 -0
  28. data/spec/spec_helper.rb +35 -0
  29. data/spec/test_helpers.rb +59 -0
  30. data/spec/unit/container_docker_build_spec.rb +325 -0
  31. data/spec/unit/container_docker_init_spec.rb +464 -0
  32. data/spec/unit/fixtures/.chef/encrypted_data_bag_secret +0 -0
  33. data/spec/unit/fixtures/.chef/trusted_certs/chef_example_com.crt +0 -0
  34. data/spec/unit/fixtures/.chef/validator.pem +1 -0
  35. data/spec/unit/fixtures/Berksfile +3 -0
  36. data/spec/unit/fixtures/cookbooks/dummy/metadata.rb +0 -0
  37. data/spec/unit/fixtures/cookbooks/nginx/metadata.rb +0 -0
  38. data/spec/unit/fixtures/environments/dev.json +0 -0
  39. data/spec/unit/fixtures/nodes/demo.json +0 -0
  40. data/spec/unit/fixtures/roles/base.json +0 -0
  41. data/spec/unit/fixtures/site-cookbooks/apt/metadata.rb +0 -0
  42. metadata +232 -0
@@ -0,0 +1,5 @@
1
+ source "https://api.berkshelf.com"
2
+
3
+ <% @cookbooks.each do |cookbook| -%>
4
+ cookbook "<%= cookbook %>"
5
+ <% end -%>
@@ -0,0 +1,16 @@
1
+ require 'chef-init'
2
+
3
+ node_name ChefInit.node_name
4
+ <% if chef_client_mode == "zero" -%>
5
+ cookbook_path ["/etc/chef/cookbooks"]
6
+ <% elsif chef_client_mode == "client" -%>
7
+ chef_server_url '<%= chef_server_url %>'
8
+ validation_client_name '<%= validation_client_name %>'
9
+ validation_key '/etc/chef/secure/validation.pem'
10
+ client_key '/etc/chef/secure/client.pem'
11
+ trusted_certs_dir '/etc/chef/secure/trusted_certs'
12
+ <% end -%>
13
+ <% unless encrypted_data_bag_secret.nil? -%>
14
+ encrypted_data_bag_secret '/etc/chef/secure/encrypted_data_bag_secret'
15
+ <% end -%>
16
+ ssl_verify_mode :verify_peer
@@ -0,0 +1,9 @@
1
+ # BASE <%= base_image %>
2
+ FROM <%= dockerfile_name %>
3
+ ADD chef/ /etc/chef/
4
+ RUN chef-init --bootstrap
5
+ <% unless include_credentials -%>
6
+ RUN rm -rf /etc/chef/secure/*
7
+ <% end -%>
8
+ ENTRYPOINT ["chef-init"]
9
+ CMD ["--onboot"]
@@ -0,0 +1 @@
1
+ <%= dockerfile_name.gsub('/','-') %>-build
@@ -0,0 +1,5 @@
1
+ module Knife
2
+ module Container
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ describe 'docker_container Ohai plugin' do
19
+
20
+ end
@@ -0,0 +1,3 @@
1
+ FROM chef/ubuntu_12.04:0.1.0
2
+ ADD chef /chef/
3
+ RUN chef-init --provision
@@ -0,0 +1,35 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'test_helpers'
19
+ require 'simplecov'
20
+ require 'json'
21
+
22
+ SimpleCov.start do
23
+ add_filter '/spec/'
24
+ end
25
+
26
+ RSpec.configure do |c|
27
+ c.include TestHelpers
28
+
29
+ c.expect_with :rspec do |config|
30
+ config.syntax = [:should, :expect]
31
+ end
32
+ c.filter_run :focus => true
33
+ c.run_all_when_everything_filtered = true
34
+ c.treat_symbols_as_metadata_keys_with_true_values = true
35
+ end
@@ -0,0 +1,59 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'fileutils'
19
+ require 'tmpdir'
20
+
21
+ module TestHelpers
22
+
23
+ # A globally accessible place where we can put some state to verify that a
24
+ # test performed a certain operation.
25
+ def self.test_state
26
+ @test_state ||= {}
27
+ end
28
+
29
+ def self.reset!
30
+ @test_state = nil
31
+ end
32
+
33
+ def test_state
34
+ TestHelpers.test_state
35
+ end
36
+
37
+ def fixtures_path
38
+ File.expand_path(File.dirname(__FILE__) + "/unit/fixtures/")
39
+ end
40
+
41
+ def project_root
42
+ File.expand_path("../..", __FILE__)
43
+ end
44
+
45
+ def reset_tempdir
46
+ clear_tempdir
47
+ tempdir
48
+ end
49
+
50
+ def clear_tempdir
51
+ FileUtils.rm_rf(@tmpdir)
52
+ @tmpdir = nil
53
+ end
54
+
55
+ def tempdir
56
+ @tmpdir ||= Dir.mktmpdir("knife-container")
57
+ File.realpath(@tmpdir)
58
+ end
59
+ end
@@ -0,0 +1,325 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'spec_helper'
19
+ require 'chef/knife/container_docker_build'
20
+ Chef::Knife::ContainerDockerBuild.load_deps
21
+
22
+ describe Chef::Knife::ContainerDockerBuild do
23
+
24
+ let(:stdout_io) { StringIO.new }
25
+ let(:stderr_io) { StringIO.new }
26
+ let(:argv) { %w[ docker/demo ] }
27
+
28
+ def stdout
29
+ stdout_io.string
30
+ end
31
+
32
+ let(:default_dockerfiles_path) do
33
+ File.expand_path("dockerfiles", fixtures_path)
34
+ end
35
+
36
+ subject(:knife) do
37
+ Chef::Knife::ContainerDockerBuild.new(argv).tap do |c|
38
+ c.stub(:output).and_return(true)
39
+ c.parse_options(argv)
40
+ c.merge_configs
41
+ end
42
+ end
43
+
44
+ describe "#run" do
45
+ before(:each) do
46
+ knife.stub(:run_berks)
47
+ knife.stub(:build_image)
48
+ knife.stub(:cleanup_artifacts)
49
+ Chef::Config.reset
50
+ Chef::Config[:chef_repo_path] = tempdir
51
+ File.stub(:exists?).with(File.join(tempdir, 'dockerfiles', 'docker', 'demo', 'chef', 'zero.rb')).and_return(true)
52
+ end
53
+
54
+ context "by default" do
55
+ let(:argv) { %w[ docker/demo ] }
56
+
57
+ it 'should parse argv, run berkshelf, build the image and cleanup the artifacts' do
58
+ expect(knife).to receive(:read_and_validate_params).and_call_original
59
+ expect(knife).to receive(:setup_config_defaults).and_call_original
60
+ expect(knife).to receive(:run_berks)
61
+ expect(knife).to receive(:build_image)
62
+ expect(knife).to receive(:cleanup_artifacts)
63
+ knife.run
64
+ end
65
+ end
66
+
67
+ context "--no-berks is passed" do
68
+ let(:argv) { %w[ docker/demo --no-berks ] }
69
+
70
+ it 'should not run berkshelf' do
71
+ expect(knife).not_to receive(:run_berks)
72
+ knife.run
73
+ end
74
+ end
75
+
76
+ context "--no-cleanup is passed" do
77
+ let(:argv) { %w[ docker/demo --no-cleanup ] }
78
+
79
+ it 'should not clean up the artifacts' do
80
+ expect(knife).not_to receive(:cleanup_artifacts)
81
+ knife.run
82
+ end
83
+ end
84
+ end
85
+
86
+ describe '#read_and_validate_params' do
87
+ let(:argv) { %W[] }
88
+
89
+ context 'argv is empty' do
90
+ it 'should should print usage and exit' do
91
+ expect(knife).to receive(:show_usage)
92
+ expect(knife.ui).to receive(:fatal)
93
+ expect { knife.run }.to raise_error(SystemExit)
94
+ end
95
+ end
96
+
97
+ context "when Berkshelf is not installed" do
98
+ let(:argv) { %w[ docker/demo ] }
99
+ let(:berks_output) { double("berks -v output", stdout: "berks not found") }
100
+
101
+ it 'should set config[:cleanup] to true' do
102
+ knife.read_and_validate_params
103
+ knife.config[:cleanup].should eql(true)
104
+ end
105
+ end
106
+
107
+ context "--no-cleanup was passed" do
108
+ let(:argv) { %w[ docker/demo --no-cleanup ] }
109
+
110
+ it 'should set config[:cleanup] to false' do
111
+ knife.read_and_validate_params
112
+ knife.config[:cleanup].should eql(false)
113
+ end
114
+ end
115
+
116
+ context "--no-berks was not passed" do
117
+ let(:argv) { %w[ docker/demo ] }
118
+
119
+ context "and Berkshelf is not installed" do
120
+ let(:berks_output) { double("berks -v output", stdout: "berks not found") }
121
+
122
+ before do
123
+ knife.stub(:shell_out).with("berks -v").and_return(berks_output)
124
+ end
125
+
126
+ it 'should set run_berks to false' do
127
+ knife.read_and_validate_params
128
+ expect(knife.config[:run_berks]).to eql(false)
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '#setup_config_defaults' do
135
+ before do
136
+ Chef::Config.reset
137
+ Chef::Config[:chef_repo_path] = tempdir
138
+ File.stub(:exists?).with(File.join(tempdir, 'dockerfiles', 'docker', 'demo', 'chef', 'zero.rb')).and_return(true)
139
+ end
140
+
141
+ let(:argv) { %w[ docker/demo ]}
142
+
143
+ context 'Chef::Config[:dockerfiles_path] has not been set' do
144
+ it 'sets dockerfiles_path to Chef::Config[:chef_repo_path]/dockerfiles' do
145
+ $stdout.stub(:write)
146
+ knife.setup_config_defaults
147
+ expect(knife.config[:dockerfiles_path]).to eql("#{Chef::Config[:chef_repo_path]}/dockerfiles")
148
+ end
149
+ end
150
+ end
151
+
152
+ describe "#run_berks" do
153
+ let(:argv) { %W[ docker/demo ] }
154
+
155
+ before(:each) do
156
+ Chef::Config.reset
157
+ Chef::Config[:chef_repo_path] = tempdir
158
+ Chef::Config[:knife][:dockerfiles_path] = default_dockerfiles_path
159
+ end
160
+
161
+ let(:docker_context) { File.join(Chef::Config[:knife][:dockerfiles_path], 'docker', 'demo') }
162
+
163
+ context "when there is no Berksfile" do
164
+ before { File.stub(:exists?).with(File.join(docker_context, 'Berksfile')).and_return(false) }
165
+
166
+ it "returns doing nothing" do
167
+ expect(knife).not_to receive(:run_berks_vendor)
168
+ expect(knife).not_to receive(:run_berks_upload)
169
+ knife.run_berks
170
+ end
171
+ end
172
+
173
+ context "when docker image was init in local mode" do
174
+ before do
175
+ File.stub(:exists?).with(File.join(docker_context, 'Berksfile')).and_return(true)
176
+ File.stub(:exists?).with(File.join(docker_context, 'chef', 'zero.rb')).and_return(true)
177
+ File.stub(:exists?).with(File.join(docker_context, 'chef', 'client.rb')).and_return(false)
178
+ knife.stub(:chef_repo).and_return(File.join(docker_context, "chef"))
179
+ end
180
+
181
+ it 'should call run_berks_vendor' do
182
+ expect(knife).to receive(:run_berks_vendor)
183
+ knife.run_berks
184
+ end
185
+ end
186
+
187
+ context "when docker image was init in client mode" do
188
+ before do
189
+ File.stub(:exists?).with(File.join(docker_context, 'Berksfile')).and_return(true)
190
+ File.stub(:exists?).with(File.join(docker_context, 'chef', 'zero.rb')).and_return(false)
191
+ File.stub(:exists?).with(File.join(docker_context, 'chef', 'client.rb')).and_return(true)
192
+ knife.stub(:chef_repo).and_return(File.join(docker_context, "chef"))
193
+ end
194
+
195
+ it 'should call run_berks_upload' do
196
+ expect(knife).to receive(:run_berks_upload)
197
+ knife.run_berks
198
+ end
199
+ end
200
+ end
201
+
202
+ describe "#run_berks_install" do
203
+ it "should call `berks install`" do
204
+ expect(knife).to receive(:run_command).with("berks install")
205
+ knife.run_berks_install
206
+ end
207
+ end
208
+
209
+ describe "#run_berks_vendor" do
210
+
211
+ before(:each) do
212
+ Chef::Config.reset
213
+ Chef::Config[:chef_repo_path] = tempdir
214
+ Chef::Config[:knife][:dockerfiles_path] = default_dockerfiles_path
215
+ knife.stub(:docker_context).and_return(File.join(default_dockerfiles_path, 'docker', 'demo'))
216
+ knife.stub(:run_berks_install)
217
+ end
218
+
219
+ let(:docker_context) { File.join(Chef::Config[:knife][:dockerfiles_path], 'docker', 'demo') }
220
+
221
+ context "cookbooks directory already exists in docker context" do
222
+ before do
223
+ File.stub(:exists?).with(File.join(docker_context, 'chef', 'cookbooks')).and_return(true)
224
+ end
225
+
226
+ context "and force-build was specified" do
227
+ let(:argv) { %w[ docker/demo --force ]}
228
+
229
+ it "should delete the existing cookbooks directory and run berks.vendor" do
230
+ expect(FileUtils).to receive(:rm_rf).with(File.join(docker_context, 'chef', 'cookbooks'))
231
+ expect(knife).to receive(:run_berks_install)
232
+ expect(knife).to receive(:run_command).with("berks vendor #{File.join(docker_context, 'chef', 'cookbooks')}")
233
+ knife.run_berks_vendor
234
+ end
235
+
236
+ end
237
+
238
+ context "and force-build was not specified" do
239
+ let(:argv) { %w[ docker-demo ] }
240
+
241
+ it "should error out" do
242
+ $stdout.stub(:write)
243
+ $stderr.stub(:write)
244
+ expect { knife.run_berks_vendor }.to raise_error(SystemExit)
245
+ end
246
+ end
247
+ end
248
+
249
+ context "cookbooks directory does not yet exist" do
250
+ before do
251
+ File.stub(:exists?).with(File.join(docker_context, 'chef', 'cookbooks')).and_return(false)
252
+ end
253
+
254
+ it "should call berks.vendor" do
255
+ expect(knife).to receive(:run_berks_install)
256
+ expect(knife).to receive(:run_command).with("berks vendor #{File.join(docker_context, 'chef', 'cookbooks')}")
257
+ knife.run_berks_vendor
258
+ end
259
+ end
260
+ end
261
+
262
+ describe "#run_berks_upload" do
263
+ before(:each) do
264
+ Chef::Config.reset
265
+ Chef::Config[:chef_repo_path] = tempdir
266
+ Chef::Config[:knife][:dockerfiles_path] = default_dockerfiles_path
267
+ knife.stub(:docker_context).and_return(File.join(default_dockerfiles_path, 'docker', 'demo'))
268
+ knife.stub(:run_berks_install)
269
+ end
270
+
271
+ let(:docker_context) { File.join(Chef::Config[:knife][:dockerfiles_path], 'docker', 'local') }
272
+
273
+ context "by default" do
274
+ before do
275
+ knife.config[:force_build] = false
276
+ end
277
+
278
+ it "should call berks install" do
279
+ knife.stub(:run_command).with("berks upload")
280
+ expect(knife).to receive(:run_berks_install)
281
+ knife.run_berks_upload
282
+ end
283
+
284
+ it "should run berks upload" do
285
+ expect(knife).to receive(:run_command).with("berks upload")
286
+ knife.run_berks_upload
287
+ end
288
+ end
289
+
290
+ context "when force-build is specified" do
291
+ before do
292
+ knife.config[:force_build] = true
293
+ end
294
+
295
+ it "should run berks upload with force" do
296
+ expect(knife).to receive(:run_command).with("berks upload --force")
297
+ knife.run_berks_upload
298
+ end
299
+ end
300
+ end
301
+
302
+ describe "#docker_build_command" do
303
+ let(:argv) { %W[ docker/demo ] }
304
+
305
+ before(:each) do
306
+ knife.config[:dockerfiles_path] = default_dockerfiles_path
307
+ end
308
+
309
+ it "should return valid command" do
310
+ expect(knife.docker_build_command).to eql("docker build -t docker/demo #{default_dockerfiles_path}/docker/demo")
311
+ end
312
+ end
313
+
314
+ describe "#cleanup_artifacts" do
315
+ let(:argv) { %w[ docker/demo ] }
316
+
317
+ context "running in server-mode" do
318
+ it "should delete the node and client objects from the Chef Server" do
319
+ expect(knife).to receive(:destroy_item).with(Chef::Node, 'docker-demo-build', 'node')
320
+ expect(knife).to receive(:destroy_item).with(Chef::ApiClient, 'docker-demo-build', 'client')
321
+ knife.cleanup_artifacts
322
+ end
323
+ end
324
+ end
325
+ end