chef-dk 0.5.0.rc.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
+