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
@@ -17,6 +17,7 @@
17
17
 
18
18
  require 'ffi_yajl'
19
19
 
20
+ require 'chef-dk/helpers'
20
21
  require 'chef-dk/service_exceptions'
21
22
  require 'chef-dk/policyfile_compiler'
22
23
  require 'chef-dk/policyfile/storage_config'
@@ -0,0 +1,104 @@
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 'chef-dk/helpers'
19
+ require 'chef-dk/policyfile/storage_config'
20
+ require 'chef-dk/service_exceptions'
21
+ require 'chef-dk/policyfile_compiler'
22
+
23
+ module ChefDK
24
+ module PolicyfileServices
25
+
26
+ class UpdateAttributes
27
+
28
+ include Policyfile::StorageConfigDelegation
29
+ include ChefDK::Helpers
30
+
31
+ attr_reader :ui
32
+ attr_reader :storage_config
33
+
34
+ def initialize(policyfile: nil, ui: nil, root_dir: nil)
35
+ @ui = ui
36
+
37
+ policyfile_rel_path = policyfile || "Policyfile.rb"
38
+ policyfile_full_path = File.expand_path(policyfile_rel_path, root_dir)
39
+ @storage_config = Policyfile::StorageConfig.new.use_policyfile(policyfile_full_path)
40
+ @updated = false
41
+ end
42
+
43
+ def run
44
+ assert_policy_and_lock_present!
45
+
46
+ if policyfile_compiler.default_attributes != policyfile_lock.default_attributes
47
+ policyfile_lock.default_attributes = policyfile_compiler.default_attributes
48
+ @updated = true
49
+ end
50
+
51
+ if policyfile_compiler.override_attributes != policyfile_lock.override_attributes
52
+ policyfile_lock.override_attributes = policyfile_compiler.override_attributes
53
+ @updated = true
54
+ end
55
+
56
+ if updated_lock?
57
+ with_file(policyfile_lock_expanded_path) do |f|
58
+ f.print(FFI_Yajl::Encoder.encode(policyfile_lock.to_lock, pretty: true ))
59
+ end
60
+ ui.msg("Updated attributes in #{policyfile_lock_expanded_path}")
61
+ else
62
+ ui.msg("Attributes already up to date")
63
+ end
64
+ rescue => error
65
+ raise PolicyfileUpdateError.new("Failed to update Policyfile lock", error)
66
+ end
67
+
68
+ def updated_lock?
69
+ @updated
70
+ end
71
+
72
+ def policyfile_content
73
+ @policyfile_content ||= IO.read(policyfile_expanded_path)
74
+ end
75
+
76
+ def policyfile_compiler
77
+ @policyfile_compiler ||= ChefDK::PolicyfileCompiler.evaluate(policyfile_content, policyfile_expanded_path, ui: ui)
78
+ end
79
+
80
+ def policyfile_lock_content
81
+ @policyfile_lock_content ||= IO.read(policyfile_lock_expanded_path)
82
+ end
83
+
84
+ def policyfile_lock
85
+ @policyfile_lock ||= begin
86
+ lock_data = FFI_Yajl::Parser.new.parse(policyfile_lock_content)
87
+ PolicyfileLock.new(storage_config, ui: ui).build_from_lock_data(lock_data)
88
+ end
89
+ end
90
+
91
+
92
+ def assert_policy_and_lock_present!
93
+ unless File.exist?(policyfile_expanded_path)
94
+ raise PolicyfileNotFound, "Policyfile not found at path #{policyfile_expanded_path}"
95
+ end
96
+ unless File.exist?(policyfile_lock_expanded_path)
97
+ raise LockfileNotFound, "Policyfile lock not found at path #{policyfile_lock_expanded_path}"
98
+ end
99
+ end
100
+
101
+ end
102
+ end
103
+ end
104
+
@@ -36,6 +36,12 @@ module ChefDK
36
36
  class LockfileNotFound < PolicyfileServiceError
37
37
  end
38
38
 
39
+ class MalformedLockfile < PolicyfileServiceError
40
+ end
41
+
42
+ class GitError < PolicyfileServiceError
43
+ end
44
+
39
45
  class ExportDirNotEmpty < PolicyfileServiceError
40
46
  end
41
47
 
@@ -70,9 +76,15 @@ module ChefDK
70
76
 
71
77
  end
72
78
 
79
+ class PolicyfileDownloadError < PolicyfileNestedException
80
+ end
81
+
73
82
  class PolicyfileInstallError < PolicyfileNestedException
74
83
  end
75
84
 
85
+ class PolicyfileUpdateError < PolicyfileNestedException
86
+ end
87
+
76
88
  class PolicyfilePushError < PolicyfileNestedException
77
89
  end
78
90
 
@@ -24,6 +24,10 @@ module ChefDK
24
24
  nil
25
25
  end
26
26
 
27
+ def print(*anything)
28
+ nil
29
+ end
30
+
27
31
  end
28
32
 
29
33
  def self.null
@@ -45,6 +49,10 @@ module ChefDK
45
49
  def msg(message)
46
50
  @out_stream.puts(message)
47
51
  end
52
+
53
+ def print(message)
54
+ @out_stream.print(message)
55
+ end
48
56
  end
49
57
  end
50
58
 
@@ -16,5 +16,5 @@
16
16
  #
17
17
 
18
18
  module ChefDK
19
- VERSION = "0.5.0.rc.1"
19
+ VERSION = "0.5.0"
20
20
  end
@@ -23,6 +23,8 @@ require 'test_helpers'
23
23
  require 'chef/workstation_config_loader'
24
24
 
25
25
  RSpec.configure do |c|
26
+ running_on_windows = (RUBY_PLATFORM =~ /mswin|mingw|windows/)
27
+
26
28
  c.include ChefDK
27
29
  c.include TestHelpers
28
30
 
@@ -35,8 +37,12 @@ RSpec.configure do |c|
35
37
 
36
38
  c.filter_run :focus => true
37
39
  c.run_all_when_everything_filtered = true
40
+ # Tests that randomly fail, but may have value.
41
+ c.filter_run_excluding :volatile => true
42
+ c.filter_run_excluding :skip_on_windows => true if running_on_windows
38
43
 
39
44
  c.mock_with(:rspec) do |mocks|
40
45
  mocks.verify_partial_doubles = true
41
46
  end
47
+
42
48
  end
@@ -73,6 +73,10 @@ module TestHelpers
73
73
  @output_stream.puts(message)
74
74
  end
75
75
 
76
+ def print(message)
77
+ @output_stream.print(message)
78
+ end
79
+
76
80
  def output
77
81
  @output_stream.string
78
82
  end
@@ -0,0 +1,283 @@
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 'shared/command_with_ui_object'
20
+ require 'chef-dk/command/diff'
21
+
22
+ describe ChefDK::Command::Diff do
23
+
24
+ it_behaves_like "a command with a UI object"
25
+
26
+ let(:params) { [] }
27
+
28
+ let(:command) do
29
+ described_class.new
30
+ end
31
+
32
+ let(:chef_config_loader) { instance_double("Chef::WorkstationConfigLoader") }
33
+
34
+ let(:chef_config) { double("Chef::Config") }
35
+
36
+ let(:config_arg) { nil }
37
+
38
+ before do
39
+ stub_const("Chef::Config", chef_config)
40
+ allow(Chef::WorkstationConfigLoader).to receive(:new).with(config_arg).and_return(chef_config_loader)
41
+ end
42
+
43
+ describe "selecting comparison bases" do
44
+
45
+ let(:ui) { TestHelpers::TestUI.new }
46
+
47
+ let(:http_client) { instance_double("ChefDK::AuthenticatedHTTP") }
48
+
49
+ let(:differ) { instance_double("ChefDK::Policyfile::Differ", run_report: nil) }
50
+
51
+ let(:pager) { instance_double("ChefDK::Pager", ui: ui) }
52
+
53
+ before do
54
+ allow(ChefDK::Pager).to receive(:new).and_return(pager)
55
+ allow(pager).to receive(:with_pager).and_yield(pager)
56
+ allow(command).to receive(:materialize_locks).and_return(nil)
57
+ allow(command).to receive(:differ).and_return(differ)
58
+ allow(command).to receive(:http_client).and_return(http_client)
59
+ command.ui = ui
60
+ end
61
+
62
+ context "when no base is given" do
63
+
64
+ it "prints an error message and exits" do
65
+ expect(command.run(params)).to eq(1)
66
+ expect(ui.output).to include("No comparison specified")
67
+ end
68
+
69
+ end
70
+
71
+ context "when server and git comparison bases are mixed" do
72
+
73
+ let(:params) { %w{ --git gitref policygroup } }
74
+
75
+ it "prints an error message and exits" do
76
+ expect(command.run(params)).to eq(1)
77
+ expect(ui.output).to include("Conflicting arguments and options: git and Policy Group comparisons cannot be mixed")
78
+ end
79
+
80
+ end
81
+
82
+ context "when specific git comparison bases are mixed with --head" do
83
+
84
+ let(:params) { %w{ --head --git gitref } }
85
+
86
+ it "prints an error message and exits" do
87
+ expect(command.run(params)).to eq(1)
88
+ expect(ui.output).to include("Conflicting git options: --head and --git are exclusive")
89
+ end
90
+
91
+ end
92
+
93
+ describe "selecting git comparison bases" do
94
+
95
+ context "when the Policyfile isn't named" do
96
+
97
+ let(:params) { %w{ --head } }
98
+
99
+ it "uses Policyfile.lock.json as the local lock" do
100
+ expect(command.run(params)).to eq(0)
101
+ expect(command.policyfile_lock_relpath).to eq("Policyfile.lock.json")
102
+ end
103
+
104
+ end
105
+
106
+ context "when the Policyfile is named" do
107
+
108
+ context "using the --head option" do
109
+
110
+ let(:params) { %w{ policies/OtherPolicy.rb --head } }
111
+
112
+ it "uses the corresponding lock as the local lock" do
113
+ expect(command.run(params)).to eq(0)
114
+ expect(command.policyfile_lock_relpath).to eq("policies/OtherPolicy.lock.json")
115
+ end
116
+
117
+ end
118
+
119
+ context "using the --git option" do
120
+
121
+ let(:params) { %w{ policies/OtherPolicy.rb --git master } }
122
+
123
+ it "uses the corresponding lock as the local lock" do
124
+ expect(command.run(params)).to eq(0)
125
+ expect(command.policyfile_lock_relpath).to eq("policies/OtherPolicy.lock.json")
126
+ end
127
+
128
+ end
129
+
130
+ end
131
+
132
+ context "when given a single commit-ish" do
133
+
134
+ let(:params) { %w{ --git master } }
135
+
136
+ it "compares the local lock to the commit" do
137
+ expect(command.run(params)).to eq(0)
138
+ expect(command.old_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::Git)
139
+ expect(command.old_base.ref).to eq("master")
140
+ expect(command.new_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::Local)
141
+ expect(command.new_base.policyfile_lock_relpath).to eq("Policyfile.lock.json")
142
+ end
143
+
144
+ end
145
+
146
+ context "when given two commit-ish names" do
147
+
148
+ let(:params) { %w{ --git master...dev-branch } }
149
+
150
+ it "compares the two commits" do
151
+ expect(command.run(params)).to eq(0)
152
+ expect(command.old_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::Git)
153
+ expect(command.old_base.ref).to eq("master")
154
+ expect(command.new_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::Git)
155
+ expect(command.new_base.ref).to eq("dev-branch")
156
+ end
157
+
158
+ end
159
+
160
+ context "when given too many commit-ish names" do
161
+
162
+ let(:params) { %w{ --git too...many...things } }
163
+
164
+ it "prints an error and exits" do
165
+ expect(command.run(params)).to eq(1)
166
+ expect(ui.output).to include("Unable to parse git comparison `too...many...things`. Only 2 references can be specified.")
167
+ end
168
+
169
+ end
170
+
171
+ context "when --head is used" do
172
+
173
+ let(:params) { %w{ --head } }
174
+
175
+ it "compares the local lock to git HEAD" do
176
+ expect(command.run(params)).to eq(0)
177
+ expect(command.old_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::Git)
178
+ expect(command.old_base.ref).to eq("HEAD")
179
+ expect(command.new_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::Local)
180
+ expect(command.new_base.policyfile_lock_relpath).to eq("Policyfile.lock.json")
181
+ end
182
+
183
+ end
184
+
185
+ end
186
+
187
+ describe "selecting policy group comparison bases" do
188
+
189
+ let(:local_lock_comparison_base) do
190
+ instance_double("ChefDK::Policyfile::ComparisonBase::Local")
191
+ end
192
+
193
+ before do
194
+ allow(command).to receive(:local_lock_comparison_base).and_return(local_lock_comparison_base)
195
+ end
196
+
197
+ context "when the local lockfile cannot be read and parsed" do
198
+
199
+ let(:params) { %w{ dev-group } }
200
+
201
+ before do
202
+ allow(local_lock_comparison_base).to receive(:lock).and_raise(ChefDK::LockfileNotFound)
203
+ end
204
+
205
+ it "prints an error and exits" do
206
+ expect(command.run(params)).to eq(1)
207
+ end
208
+
209
+ end
210
+
211
+ context "when the local lockfile can be read and parsed" do
212
+ before do
213
+ allow(local_lock_comparison_base).to receive(:lock).and_return({"name" => "example-policy"})
214
+ allow(command).to receive(:differ).and_return(differ)
215
+ command.ui = ui
216
+ end
217
+
218
+ context "when the Policyfile isn't named" do
219
+
220
+ let(:params) { %w{ dev-group } }
221
+
222
+ it "uses Policyfile.lock.json as the local lock" do
223
+ expect(command.run(params)).to eq(0)
224
+ expect(command.policyfile_lock_relpath).to eq("Policyfile.lock.json")
225
+ end
226
+
227
+ end
228
+
229
+ context "when the Policyfile is named" do
230
+
231
+ let(:params) { %w{ policies/SomePolicy.rb dev-group } }
232
+
233
+ it "uses the corresponding lock as the local lock" do
234
+ expect(command.run(params)).to eq(0)
235
+ expect(command.policyfile_lock_relpath).to eq("policies/SomePolicy.lock.json")
236
+ end
237
+
238
+ end
239
+
240
+ context "when given a single policy group name" do
241
+
242
+ let(:params) { %w{ dev-group } }
243
+
244
+ it "compares the policy group's lock to the local lock" do
245
+ expect(command.run(params)).to eq(0)
246
+ expect(command.old_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::PolicyGroup)
247
+ expect(command.old_base.group).to eq("dev-group")
248
+ expect(command.new_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::Local)
249
+ expect(command.new_base.policyfile_lock_relpath).to eq("Policyfile.lock.json")
250
+ end
251
+
252
+ end
253
+
254
+ context "when given two policy group names" do
255
+
256
+ let(:params) { %w{ prod-group...stage-group } }
257
+
258
+ it "compares the two locks" do
259
+ expect(command.run(params)).to eq(0)
260
+ expect(command.old_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::PolicyGroup)
261
+ expect(command.old_base.group).to eq("prod-group")
262
+ expect(command.new_base).to be_a_kind_of(ChefDK::Policyfile::ComparisonBase::PolicyGroup)
263
+ expect(command.new_base.group).to eq("stage-group")
264
+ end
265
+
266
+ end
267
+
268
+ context "when given too many policy group names" do
269
+
270
+ let(:params) { %w{ prod...stage...dev } }
271
+
272
+ it "prints an error and exits" do
273
+ expect(command.run(params)).to eq(1)
274
+ expect(ui.output).to include("Unable to parse policy group comparison `prod...stage...dev`. Only 2 references can be specified.")
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+ end
281
+ end
282
+ end
283
+