chef-dk 0.5.0.rc.1 → 0.5.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +63 -24
  3. data/lib/chef-dk/builtin_commands.rb +2 -0
  4. data/lib/chef-dk/command/diff.rb +312 -0
  5. data/lib/chef-dk/command/push.rb +1 -1
  6. data/lib/chef-dk/command/shell_init.rb +21 -3
  7. data/lib/chef-dk/command/update.rb +28 -5
  8. data/lib/chef-dk/configurable.rb +1 -1
  9. data/lib/chef-dk/exceptions.rb +3 -0
  10. data/lib/chef-dk/pager.rb +106 -0
  11. data/lib/chef-dk/policyfile/chef_repo_cookbook_source.rb +114 -0
  12. data/lib/chef-dk/policyfile/comparison_base.rb +124 -0
  13. data/lib/chef-dk/policyfile/cookbook_sources.rb +1 -0
  14. data/lib/chef-dk/policyfile/differ.rb +266 -0
  15. data/lib/chef-dk/policyfile/dsl.rb +26 -3
  16. data/lib/chef-dk/policyfile/uploader.rb +4 -5
  17. data/lib/chef-dk/policyfile_compiler.rb +8 -0
  18. data/lib/chef-dk/policyfile_lock.rb +135 -3
  19. data/lib/chef-dk/policyfile_services/install.rb +1 -0
  20. data/lib/chef-dk/policyfile_services/update_attributes.rb +104 -0
  21. data/lib/chef-dk/service_exceptions.rb +12 -0
  22. data/lib/chef-dk/ui.rb +8 -0
  23. data/lib/chef-dk/version.rb +1 -1
  24. data/spec/spec_helper.rb +6 -0
  25. data/spec/test_helpers.rb +4 -0
  26. data/spec/unit/command/diff_spec.rb +283 -0
  27. data/spec/unit/command/shell_init_spec.rb +19 -2
  28. data/spec/unit/command/update_spec.rb +96 -0
  29. data/spec/unit/command/verify_spec.rb +0 -6
  30. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/Berksfile +3 -0
  31. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/README.md +4 -0
  32. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/chefignore +96 -0
  33. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/metadata.rb +9 -0
  34. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/recipes/default.rb +8 -0
  35. data/spec/unit/pager_spec.rb +119 -0
  36. data/spec/unit/policyfile/chef_repo_cookbook_source_spec.rb +66 -0
  37. data/spec/unit/policyfile/comparison_base_spec.rb +343 -0
  38. data/spec/unit/policyfile/differ_spec.rb +687 -0
  39. data/spec/unit/policyfile_evaluation_spec.rb +87 -0
  40. data/spec/unit/policyfile_lock_build_spec.rb +247 -8
  41. data/spec/unit/policyfile_lock_serialization_spec.rb +47 -0
  42. data/spec/unit/policyfile_services/export_repo_spec.rb +2 -0
  43. data/spec/unit/policyfile_services/push_spec.rb +2 -0
  44. data/spec/unit/policyfile_services/update_attributes_spec.rb +217 -0
  45. metadata +62 -6
@@ -20,6 +20,7 @@ require 'chef-dk/command/shell_init'
20
20
 
21
21
  describe ChefDK::Command::ShellInit do
22
22
 
23
+ let(:expected_path) { [omnibus_bin_dir, user_bin_dir, omnibus_embedded_bin_dir, ENV['PATH']].join(File::PATH_SEPARATOR) }
23
24
  let(:stdout_io) { StringIO.new }
24
25
  let(:stderr_io) { StringIO.new }
25
26
 
@@ -32,7 +33,6 @@ describe ChefDK::Command::ShellInit do
32
33
 
33
34
  shared_context "shell init script" do |shell|
34
35
  let(:user_bin_dir) { File.expand_path(File.join(Gem.user_dir, 'bin')) }
35
- let(:expected_path) { [omnibus_bin_dir, user_bin_dir, omnibus_embedded_bin_dir, ENV['PATH']].join(File::PATH_SEPARATOR) }
36
36
  let(:expected_gem_root) { Gem.default_dir.to_s }
37
37
  let(:expected_gem_home) { Gem.user_dir }
38
38
  let(:expected_gem_path) { Gem.path.join(File::PATH_SEPARATOR) }
@@ -107,6 +107,23 @@ EOH
107
107
  end
108
108
  end
109
109
 
110
+ context 'for fish' do
111
+ before do
112
+ stub_const('File::PATH_SEPARATOR', ':')
113
+ end
114
+ let(:expected_path) { [omnibus_bin_dir, user_bin_dir, omnibus_embedded_bin_dir, ENV['PATH']].join(':').split(':').join('" "') }
115
+ let(:expected_environment_commands) do
116
+ <<-EOH
117
+ set -gx PATH "#{expected_path}" 2>/dev/null;
118
+ set -gx GEM_ROOT "#{expected_gem_root}";
119
+ set -gx GEM_HOME "#{expected_gem_home}";
120
+ set -gx GEM_PATH "#{expected_gem_path}";
121
+ EOH
122
+ end
123
+
124
+ include_context 'shell init script', 'fish'
125
+ end
126
+
110
127
  ['powershell', 'posh'].each do |shell|
111
128
  context "for #{shell}" do
112
129
  it_behaves_like "a powershell script", shell
@@ -131,7 +148,7 @@ EOH
131
148
  it "exits with an error message" do
132
149
  expect(command_instance.run(argv)).to eq(1)
133
150
  expect(stderr_io.string).to include("Shell `nosuchsh' is not currently supported")
134
- expect(stderr_io.string).to include("Supported shells are: bash zsh sh powershell posh")
151
+ expect(stderr_io.string).to include("Supported shells are: bash fish zsh sh powershell posh")
135
152
  end
136
153
 
137
154
  end
@@ -33,6 +33,8 @@ describe ChefDK::Command::Update do
33
33
 
34
34
  let(:install_service) { instance_double(ChefDK::PolicyfileServices::Install) }
35
35
 
36
+ let(:update_attrs_service) { instance_double(ChefDK::PolicyfileServices::UpdateAttributes) }
37
+
36
38
  it "disables debug by default" do
37
39
  expect(command.debug?).to be(false)
38
40
  end
@@ -46,6 +48,22 @@ describe ChefDK::Command::Update do
46
48
  end
47
49
  end
48
50
 
51
+ context "when attributes update mode is set" do
52
+
53
+ let(:params) { ["-a"] }
54
+
55
+ it "enables attributes update mode" do
56
+ expect(command.update_attributes?).to be(true)
57
+ end
58
+
59
+ it "creates an attributes update service object" do
60
+ expect(ChefDK::PolicyfileServices::UpdateAttributes).to receive(:new).
61
+ with(policyfile: nil, ui: command.ui, root_dir: Dir.pwd).
62
+ and_return(update_attrs_service)
63
+ expect(command.attributes_updater).to eq(update_attrs_service)
64
+ end
65
+ end
66
+
49
67
  context "with no arguments" do
50
68
 
51
69
  it "does not specify a policyfile relative path" do
@@ -140,6 +158,84 @@ Error: install failed
140
158
  Reason: (StandardError) some operation failed
141
159
 
142
160
 
161
+ E
162
+
163
+ expected_error_text << backtrace.join("\n") << "\n"
164
+
165
+ command.run
166
+ expect(ui.output).to eq(expected_error_text)
167
+ end
168
+ end
169
+
170
+ end
171
+
172
+ end
173
+
174
+ describe "running attributes update" do
175
+
176
+ let(:params) { ["-a"] }
177
+
178
+ let(:ui) { TestHelpers::TestUI.new }
179
+
180
+ before do
181
+ command.ui = ui
182
+ allow(command).to receive(:attributes_updater).and_return(update_attrs_service)
183
+ end
184
+
185
+ context "when the command runs successfully" do
186
+
187
+ before do
188
+ expect(update_attrs_service).to receive(:run)
189
+ end
190
+
191
+ it "exits with success" do
192
+ expect(command.run).to eq(0)
193
+ end
194
+ end
195
+
196
+ context "when the command fails" do
197
+
198
+ let(:backtrace) { caller[0...3] }
199
+
200
+ let(:cause) do
201
+ e = StandardError.new("some operation failed")
202
+ e.set_backtrace(backtrace)
203
+ e
204
+ end
205
+
206
+ let(:exception) do
207
+ ChefDK::PolicyfileUpdateError.new("Failed to update Policyfile lock", cause)
208
+ end
209
+
210
+ before do
211
+ expect(update_attrs_service).to receive(:run).and_raise(exception)
212
+ end
213
+
214
+ it "exits 1" do
215
+ expect(command.run).to eq(1)
216
+ end
217
+
218
+ it "displays the exception and cause" do
219
+ expected_error_text=<<-E
220
+ Error: Failed to update Policyfile lock
221
+ Reason: (StandardError) some operation failed
222
+
223
+ E
224
+
225
+ command.run
226
+ expect(ui.output).to eq(expected_error_text)
227
+ end
228
+
229
+ context "and debug is enabled" do
230
+
231
+ let(:params) { ["-a", "-D"] }
232
+
233
+ it "displays the exception and cause with backtrace" do
234
+ expected_error_text=<<-E
235
+ Error: Failed to update Policyfile lock
236
+ Reason: (StandardError) some operation failed
237
+
238
+
143
239
  E
144
240
 
145
241
  expected_error_text << backtrace.join("\n") << "\n"
@@ -280,12 +280,6 @@ describe ChefDK::Command::Verify do
280
280
  expect(stdout).to include("my friend everything is good...")
281
281
  end
282
282
 
283
- it "should report the output of the first verification first" do
284
- index_first = stdout.index("you are good to go...")
285
- index_second = stdout.index("my friend everything is good...")
286
- expect(index_second > index_first).to be true
287
- end
288
-
289
283
  context "and components are filtered by CLI args" do
290
284
 
291
285
  let(:command_options) { [ "successful_comp_2" ] }
@@ -0,0 +1,3 @@
1
+ source "https://supermarket.getchef.com"
2
+
3
+ metadata
@@ -0,0 +1,4 @@
1
+ # local-cookbook
2
+
3
+ TODO: Enter the cookbook description here.
4
+
@@ -0,0 +1,96 @@
1
+ # Put files/directories that should be ignored in this file when uploading
2
+ # or sharing to the community site.
3
+ # Lines that start with '# ' are comments.
4
+
5
+ # OS generated files #
6
+ ######################
7
+ .DS_Store
8
+ Icon?
9
+ nohup.out
10
+ ehthumbs.db
11
+ Thumbs.db
12
+
13
+ # SASS #
14
+ ########
15
+ .sass-cache
16
+
17
+ # EDITORS #
18
+ ###########
19
+ \#*
20
+ .#*
21
+ *~
22
+ *.sw[a-z]
23
+ *.bak
24
+ REVISION
25
+ TAGS*
26
+ tmtags
27
+ *_flymake.*
28
+ *_flymake
29
+ *.tmproj
30
+ .project
31
+ .settings
32
+ mkmf.log
33
+
34
+ ## COMPILED ##
35
+ ##############
36
+ a.out
37
+ *.o
38
+ *.pyc
39
+ *.so
40
+ *.com
41
+ *.class
42
+ *.dll
43
+ *.exe
44
+ */rdoc/
45
+
46
+ # Testing #
47
+ ###########
48
+ .watchr
49
+ .rspec
50
+ spec/*
51
+ spec/fixtures/*
52
+ test/*
53
+ features/*
54
+ Guardfile
55
+ Procfile
56
+
57
+ # SCM #
58
+ #######
59
+ .git
60
+ */.git
61
+ .gitignore
62
+ .gitmodules
63
+ .gitconfig
64
+ .gitattributes
65
+ .svn
66
+ */.bzr/*
67
+ */.hg/*
68
+ */.svn/*
69
+
70
+ # Berkshelf #
71
+ #############
72
+ Berksfile
73
+ Berksfile.lock
74
+ cookbooks/*
75
+ tmp
76
+
77
+ # Cookbooks #
78
+ #############
79
+ CONTRIBUTING
80
+ CHANGELOG*
81
+
82
+ # Strainer #
83
+ ############
84
+ Colanderfile
85
+ Strainerfile
86
+ .colander
87
+ .strainer
88
+
89
+ # Vagrant #
90
+ ###########
91
+ .vagrant
92
+ Vagrantfile
93
+
94
+ # Travis #
95
+ ##########
96
+ .travis.yml
@@ -0,0 +1,9 @@
1
+ name 'cookbook-with-a-dep'
2
+ maintainer ''
3
+ maintainer_email ''
4
+ license ''
5
+ description 'Installs/Configures local-cookbook'
6
+ long_description 'Installs/Configures local-cookbook'
7
+ version '9.9.9'
8
+
9
+ depends 'another-local-cookbook', '~> 0.1'
@@ -0,0 +1,8 @@
1
+ #
2
+ # Cookbook Name:: local-cookbook
3
+ # Recipe:: default
4
+ #
5
+ # Copyright (C) 2014
6
+ #
7
+ #
8
+ #
@@ -0,0 +1,119 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2015 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-dk/pager'
20
+
21
+ describe ChefDK::Pager do
22
+
23
+ context "with default options" do
24
+
25
+ subject(:pager) { ChefDK::Pager.new }
26
+
27
+ it "gives ENV for env" do
28
+ expect(pager.env).to eq(ENV)
29
+ end
30
+
31
+ it "checks stdout for TTY" do
32
+ expect($stdout).to receive(:tty?).twice.and_call_original
33
+ expect(pager.have_tty?).to eq($stdout.tty?)
34
+ end
35
+
36
+ it "enables paging" do
37
+ expect(pager).to receive(:env).and_return({"PAGER" => "less"})
38
+ expect(pager).to receive(:have_tty?).and_return(true)
39
+ expect(pager.pager_enabled?).to be(true)
40
+ end
41
+ end
42
+
43
+ context "with paging enabled" do
44
+
45
+ subject(:pager) do
46
+ ChefDK::Pager.new(enable_pager: true).tap do |p|
47
+ allow(p).to receive(:env).and_return({"PAGER" => "less"})
48
+ allow(p).to receive(:have_tty?).and_return(true)
49
+ end
50
+ end
51
+
52
+ let(:pipe_read) { instance_double("IO") }
53
+ let(:pipe_write) { instance_double("IO") }
54
+
55
+ let(:pager_env) { { "LESS" => "-FRX", "LV" => "-c" } }
56
+
57
+ before do
58
+ allow(IO).to receive(:pipe).and_return([pipe_read, pipe_write])
59
+ end
60
+
61
+ it "provides a UI object with stdout set to a pipe" do
62
+ expect(pager.ui.out_stream).to eq(pipe_write)
63
+ end
64
+
65
+ it "starts the pager" do
66
+ expect(Kernel).to receive(:trap).with(:INT, "IGNORE")
67
+ expect(Process).to receive(:spawn).with(pager_env, "less", in: pipe_read).and_return(12345)
68
+ expect(pipe_read).to receive(:close)
69
+ pager.start
70
+ end
71
+
72
+ it "waits for the pager to exit" do
73
+ expect(Kernel).to receive(:trap).with(:INT, "IGNORE")
74
+ expect(Process).to receive(:spawn).with(pager_env, "less", in: pipe_read).and_return(12345)
75
+ expect(pipe_read).to receive(:close)
76
+ pager.start
77
+
78
+ expect(pipe_write).to receive(:close)
79
+ expect(Process).to receive(:waitpid).with(12345)
80
+ pager.wait
81
+ end
82
+ end
83
+
84
+ context "with paging disabled" do
85
+
86
+ subject(:pager) do
87
+ ChefDK::Pager.new(enable_pager: false).tap do |p|
88
+ allow(p).to receive(:env).and_return({"PAGER" => "less"})
89
+ allow(p).to receive(:have_tty?).and_return(true)
90
+ end
91
+ end
92
+
93
+ before do
94
+ expect(IO).to_not receive(:pipe)
95
+ end
96
+
97
+
98
+ it "provides a UI with stdout set to stdout" do
99
+ expect(pager.ui.out_stream).to eq($stdout)
100
+ end
101
+
102
+ it "no-ops on pager start" do
103
+ expect(Kernel).to_not receive(:trap)
104
+ expect(Process).to_not receive(:spawn)
105
+ pager.start
106
+ end
107
+
108
+ it "no-ops on pager wait" do
109
+ expect(Kernel).to_not receive(:trap)
110
+ expect(Process).to_not receive(:spawn)
111
+ pager.start
112
+
113
+ expect(Process).to_not receive(:waitpid)
114
+ pager.wait
115
+ end
116
+
117
+ end
118
+ end
119
+
@@ -0,0 +1,66 @@
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-dk/policyfile/chef_repo_cookbook_source'
20
+
21
+ describe ChefDK::Policyfile::ChefRepoCookbookSource do
22
+
23
+ let(:repo_path) {
24
+ File.expand_path("spec/unit/fixtures/local_path_cookbooks", project_root)
25
+ }
26
+
27
+ let(:cookbook_source) { ChefDK::Policyfile::ChefRepoCookbookSource.new(repo_path) }
28
+
29
+ let(:local_repo_universe) {
30
+ {
31
+ "another-local-cookbook"=>{
32
+ "0.1.0"=>[],
33
+ },
34
+ "local-cookbook"=>{
35
+ "2.3.4"=>[],
36
+ },
37
+ "cookbook-with-a-dep"=>{
38
+ "9.9.9"=>[["another-local-cookbook", "~> 0.1"]],
39
+ },
40
+ "noignore"=>{
41
+ "0.1.0"=>[],
42
+ },
43
+ }
44
+ }
45
+ it "fetches the universe graph" do
46
+ actual_universe = cookbook_source.universe_graph
47
+ expect(actual_universe).to eql(local_repo_universe)
48
+ end
49
+
50
+ it "generates location options for a cookbook from the given graph" do
51
+ expected_opts = { path: File.join(repo_path, "local-cookbook"), version: "2.3.4" }
52
+ expect(cookbook_source.source_options_for("local-cookbook", "2.3.4")).to eq(expected_opts)
53
+ end
54
+
55
+ it "will append a cookbooks directory to the path if it finds it" do
56
+ expect(Dir).to receive(:exist?).with("#{repo_path}/cookbooks").and_return(true)
57
+ expect(cookbook_source.path).to eql("#{repo_path}/cookbooks")
58
+ end
59
+
60
+ it "the private setter will append a cookbooks directory to the path if finds it" do
61
+ expect(cookbook_source.path).to eql(repo_path)
62
+ expect(Dir).to receive(:exist?).with("#{repo_path}/cookbooks").and_return(true)
63
+ cookbook_source.send(:path=, repo_path)
64
+ expect(cookbook_source.path).to eql("#{repo_path}/cookbooks")
65
+ end
66
+ end