knife-container 0.2.0

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 (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