chef-dk 0.6.2 → 0.7.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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/lib/chef-dk/builtin_commands.rb +7 -0
  4. data/lib/chef-dk/command/env.rb +90 -0
  5. data/lib/chef-dk/command/export.rb +22 -1
  6. data/lib/chef-dk/command/generate.rb +1 -1
  7. data/lib/chef-dk/command/provision.rb +43 -0
  8. data/lib/chef-dk/command/push_archive.rb +126 -0
  9. data/lib/chef-dk/command/show_policy.rb +166 -0
  10. data/lib/chef-dk/command/verify.rb +58 -1
  11. data/lib/chef-dk/cookbook_omnifetch.rb +3 -2
  12. data/lib/chef-dk/exceptions.rb +27 -0
  13. data/lib/chef-dk/helpers.rb +29 -0
  14. data/lib/chef-dk/policyfile/chef_repo_cookbook_source.rb +8 -0
  15. data/lib/chef-dk/policyfile/chef_server_cookbook_source.rb +8 -0
  16. data/lib/chef-dk/policyfile/community_cookbook_source.rb +8 -0
  17. data/lib/chef-dk/policyfile/cookbook_locks.rb +76 -6
  18. data/lib/chef-dk/policyfile/dsl.rb +10 -5
  19. data/lib/chef-dk/policyfile/lister.rb +230 -0
  20. data/lib/chef-dk/policyfile/null_cookbook_source.rb +8 -0
  21. data/lib/chef-dk/policyfile_compiler.rb +35 -2
  22. data/lib/chef-dk/policyfile_lock.rb +43 -0
  23. data/lib/chef-dk/policyfile_services/clean_policies.rb +94 -0
  24. data/lib/chef-dk/policyfile_services/export_repo.rb +103 -16
  25. data/lib/chef-dk/policyfile_services/push_archive.rb +173 -0
  26. data/lib/chef-dk/policyfile_services/show_policy.rb +237 -0
  27. data/lib/chef-dk/service_exceptions.rb +21 -0
  28. data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +1 -0
  29. data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +2 -40
  30. data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +0 -2
  31. data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen.yml.erb +2 -2
  32. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe_spec.rb.erb +1 -1
  33. data/lib/chef-dk/version.rb +1 -1
  34. data/spec/unit/command/env_spec.rb +52 -0
  35. data/spec/unit/command/exec_spec.rb +2 -2
  36. data/spec/unit/command/export_spec.rb +13 -0
  37. data/spec/unit/command/provision_spec.rb +56 -0
  38. data/spec/unit/command/push_archive_spec.rb +153 -0
  39. data/spec/unit/command/show_policy_spec.rb +235 -0
  40. data/spec/unit/command/verify_spec.rb +1 -0
  41. data/spec/unit/helpers_spec.rb +68 -0
  42. data/spec/unit/policyfile/cookbook_locks_spec.rb +107 -1
  43. data/spec/unit/policyfile/lister_spec.rb +256 -0
  44. data/spec/unit/policyfile_demands_spec.rb +202 -10
  45. data/spec/unit/policyfile_evaluation_spec.rb +30 -4
  46. data/spec/unit/policyfile_lock_serialization_spec.rb +45 -0
  47. data/spec/unit/policyfile_services/clean_policies_spec.rb +236 -0
  48. data/spec/unit/policyfile_services/export_repo_spec.rb +99 -6
  49. data/spec/unit/policyfile_services/push_archive_spec.rb +345 -0
  50. data/spec/unit/policyfile_services/show_policy_spec.rb +839 -0
  51. metadata +139 -8
@@ -40,6 +40,7 @@ describe ChefDK::Command::Verify do
40
40
  "test-kitchen",
41
41
  "chef-client",
42
42
  "chef-dk",
43
+ "chef-provisioning",
43
44
  "chefspec",
44
45
  "rubocop",
45
46
  "fauxhai",
@@ -0,0 +1,68 @@
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/helpers'
20
+
21
+ describe ChefDK::Helpers do
22
+
23
+ let (:helpers) do
24
+ ChefDK::Helpers.send(:reset!)
25
+ ChefDK::Helpers
26
+ end
27
+
28
+ let(:env) { {} }
29
+
30
+ describe "chefdk_home" do
31
+ before do
32
+ allow(ENV).to receive(:[]) do |k|
33
+ env[k]
34
+ end
35
+ allow(Chef::Platform).to receive(:windows?).and_return(false)
36
+ end
37
+
38
+ context 'when CHEFDK_HOME is set' do
39
+ let(:env) { {'CHEFDK_HOME' => 'foo' } }
40
+ it "returns CHEFDK_HOME" do
41
+ expect(helpers.chefdk_home).to eq(env['CHEFDK_HOME'])
42
+ end
43
+ end
44
+
45
+ context 'when CHEFDK_HOME is not set' do
46
+ context 'on windows' do
47
+ before do
48
+ allow(Chef::Platform).to receive(:windows?).and_return(true)
49
+ end
50
+
51
+ let(:env) { { 'LOCALAPPDATA' => 'C:\\foo' } }
52
+
53
+ it 'uses LOCALAPPDATA' do
54
+ expect(File).to receive(:join).with(env['LOCALAPPDATA'], 'chefdk').and_return('chefdkdefaulthome')
55
+ expect(helpers.chefdk_home).to eq('chefdkdefaulthome')
56
+ end
57
+ end
58
+
59
+ context 'on *nix' do
60
+ it 'uses LOCALAPPDATA' do
61
+ expect(File).to receive(:expand_path).with('~/.chefdk').and_return('chefdkdefaulthome')
62
+ expect(helpers.chefdk_home).to eq('chefdkdefaulthome')
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -209,9 +209,11 @@ describe ChefDK::Policyfile::LocalCookbook do
209
209
 
210
210
  let(:storage_config) { ChefDK::Policyfile::StorageConfig.new }
211
211
 
212
+ let(:scm_profiler) { instance_double("ChefDK::CookbookProfiler::Git", profile_data: {}) }
213
+
212
214
  let(:cookbook_lock) do
213
215
  lock = described_class.new(cookbook_name, storage_config)
214
- allow(lock).to receive(:scm_info).and_return({})
216
+ allow(lock).to receive(:scm_profiler).and_return(scm_profiler)
215
217
  lock
216
218
  end
217
219
 
@@ -254,6 +256,11 @@ describe ChefDK::Policyfile::LocalCookbook do
254
256
  path
255
257
  end
256
258
 
259
+ # everywhere else, #scm_profiler is stubbed, we need the unstubbed version
260
+ let(:cookbook_lock) do
261
+ described_class.new(cookbook_name, storage_config)
262
+ end
263
+
257
264
  before do
258
265
  cookbook_lock.source = cookbook_source_path
259
266
  end
@@ -342,6 +349,11 @@ describe ChefDK::Policyfile::LocalCookbook do
342
349
  expect(cookbook_lock.source_options).to eq(expected)
343
350
  end
344
351
 
352
+ it "doesn't refresh scm_data when #lock_data is called" do
353
+ allow(scm_profiler).to receive(:profile_data).and_raise("This shouldn't get called")
354
+ cookbook_lock.lock_data
355
+ end
356
+
345
357
  context "after the data has been refreshed" do
346
358
 
347
359
  before do
@@ -421,3 +433,97 @@ describe ChefDK::Policyfile::LocalCookbook do
421
433
  end
422
434
 
423
435
  end
436
+
437
+ describe ChefDK::Policyfile::ArchivedCookbook do
438
+
439
+ let(:cookbook_name) { "nginx" }
440
+
441
+ let(:storage_config) { ChefDK::Policyfile::StorageConfig.new }
442
+
443
+ let(:wrapped_cookbook_lock_data) do
444
+ {
445
+ "identifier" => "abc123",
446
+ "dotted_decimal_identifier" => "111.222.333",
447
+ "version" => "1.2.3",
448
+ "source" => "../my_repo/nginx",
449
+ "source_options" => {
450
+ # when getting the cookbook location spec, source options needs to have
451
+ # symbolic keys, so a round trip via LocalCookbook#build_from_lock_data
452
+ # will result in this being a symbol
453
+ :path => "../my_repo/nginx"
454
+ },
455
+ "cache_key" => nil,
456
+ "scm_info" => {}
457
+ }
458
+ end
459
+
460
+ let(:wrapped_cookbook_lock) do
461
+ lock = ChefDK::Policyfile::LocalCookbook.new(cookbook_name, storage_config)
462
+ allow(lock).to receive(:scm_info).and_return({})
463
+ lock.build_from_lock_data(wrapped_cookbook_lock_data)
464
+ lock
465
+ end
466
+
467
+ let(:cookbook_lock) do
468
+ described_class.new(wrapped_cookbook_lock, storage_config)
469
+ end
470
+
471
+ let(:archived_cookbook_path) { File.join(storage_config.relative_paths_root, "cookbooks", "nginx-111.222.333") }
472
+
473
+ it "sets cookbook_path to the path within the archive" do
474
+ expect(cookbook_lock.cookbook_path).to eq(archived_cookbook_path)
475
+ end
476
+
477
+ it "implements build_from_lock_data" do
478
+ msg = "ArchivedCookbook cannot be built from lock data, it can only wrap an existing lock object"
479
+ expect { cookbook_lock.build_from_lock_data({}) }.to raise_error(NotImplementedError, msg)
480
+ end
481
+
482
+ it "implements validate!" do
483
+ expect(cookbook_lock.validate!).to be(true)
484
+ end
485
+
486
+ it "implements refresh!" do
487
+ expect(cookbook_lock.refresh!).to be(true)
488
+ end
489
+
490
+ it "implements installed?" do
491
+ allow(File).to receive(:exist?).with(archived_cookbook_path).and_return(false)
492
+ allow(File).to receive(:directory?).with(archived_cookbook_path).and_return(false)
493
+ expect(cookbook_lock.installed?).to be(false)
494
+ allow(File).to receive(:exist?).with(archived_cookbook_path).and_return(true)
495
+ allow(File).to receive(:directory?).with(archived_cookbook_path).and_return(true)
496
+ expect(cookbook_lock.installed?).to be(true)
497
+ end
498
+
499
+ describe "delegated behavior" do
500
+
501
+ it "sets the identifier" do
502
+ expect(cookbook_lock.identifier).to eq("abc123")
503
+ end
504
+
505
+ it "sets the dotted_decimal_identifier" do
506
+ expect(cookbook_lock.dotted_decimal_identifier).to eq("111.222.333")
507
+ end
508
+
509
+ it "sets the version" do
510
+ expect(cookbook_lock.version).to eq("1.2.3")
511
+ end
512
+
513
+ it "sets the source attribute" do
514
+ expect(cookbook_lock.source).to eq("../my_repo/nginx")
515
+ end
516
+
517
+ it "sets the source options, symbolizing keys so the data is compatible with CookbookLocationSpecification" do
518
+ expected = { path: "../my_repo/nginx" }
519
+ expect(cookbook_lock.source_options).to eq(expected)
520
+ end
521
+
522
+ it "returns unchanged data when calling to_lock" do
523
+ expect(cookbook_lock.to_lock).to eq(wrapped_cookbook_lock_data)
524
+ end
525
+
526
+
527
+ end
528
+
529
+ end
@@ -0,0 +1,256 @@
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/policyfile/lister'
20
+
21
+ describe ChefDK::Policyfile::Lister do
22
+
23
+ def api_url(org_specific_path)
24
+ "https://chef.example/organizations/myorg/#{org_specific_path}"
25
+ end
26
+
27
+
28
+ let(:config) do
29
+ double("Chef::Config",
30
+ chef_server_url: "https://localhost:10443",
31
+ client_key: "/path/to/client/key.pem",
32
+ node_name: "deuce")
33
+ end
34
+
35
+ let(:http_client) { instance_double(ChefDK::AuthenticatedHTTP) }
36
+
37
+ subject(:info_fetcher) do
38
+ described_class.new(config: config)
39
+ end
40
+
41
+ it "configures an HTTP client" do
42
+ expect(ChefDK::AuthenticatedHTTP).to receive(:new).with("https://localhost:10443",
43
+ signing_key_filename: "/path/to/client/key.pem",
44
+ client_name: "deuce")
45
+ info_fetcher.http_client
46
+ end
47
+
48
+
49
+ context "when the data is fetched successfully from the server" do
50
+
51
+ before do
52
+ allow(info_fetcher).to receive(:http_client).and_return(http_client)
53
+
54
+ allow(http_client).to receive(:get).with("policy_groups").and_return(policy_group_list_data)
55
+ allow(http_client).to receive(:get).with("policies").and_return(policy_list_data)
56
+ end
57
+
58
+ context "when the server has no policies or groups" do
59
+
60
+ let(:policy_group_list_data) { {} }
61
+ let(:policy_list_data) { {} }
62
+
63
+ it "gives a Hash of policy revisions by policy name" do
64
+ expect(info_fetcher.policies_by_name).to eq({})
65
+ end
66
+
67
+ it "gives a Hash of policy revisions by policy group" do
68
+ expect(info_fetcher.policies_by_group).to eq({})
69
+ end
70
+
71
+ it "is empty" do
72
+ expect(info_fetcher).to be_empty
73
+ end
74
+
75
+ it "has no active revisions" do
76
+ expect(info_fetcher.active_revisions).to be_empty
77
+ end
78
+ end
79
+
80
+ context "when the server has policies and groups" do
81
+ ##
82
+ # Example API response data copied from oc-chef-pedant:
83
+
84
+ let(:policy_list_data) do
85
+ {
86
+ "appserver" => {
87
+ "uri" => api_url("policies/appserver"),
88
+ "revisions" => {
89
+ "1111111111111111111111111111111111111111" => {},
90
+ "2222222222222222222222222222222222222222" => {},
91
+ "3333333333333333333333333333333333333333" => {},
92
+ "4444444444444444444444444444444444444444" => {}
93
+ }
94
+ },
95
+ "db" => {
96
+ "uri" => api_url("policies/db"),
97
+ "revisions" => {
98
+ "6666666666666666666666666666666666666666" => {},
99
+ "7777777777777777777777777777777777777777" => {},
100
+ "8888888888888888888888888888888888888888" => {},
101
+ "9999999999999999999999999999999999999999" => {}
102
+ }
103
+ },
104
+ "cache" => {
105
+ "uri" => api_url("policies/cache"),
106
+ "revisions" => {
107
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {},
108
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" => {}
109
+ }
110
+ }
111
+ }
112
+ end
113
+
114
+ let(:dev_group_data) do
115
+ {
116
+ "uri" => api_url("policy_groups/dev"),
117
+ "policies" => {
118
+ "db" => { "revision_id" => "6666666666666666666666666666666666666666" },
119
+ "appserver" => { "revision_id" => "1111111111111111111111111111111111111111" },
120
+ "cache" => { "revision_id" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }
121
+ }
122
+ }
123
+ end
124
+
125
+ let(:test_group_data) do
126
+ {
127
+ "uri" => api_url("policy_groups/test"),
128
+ "policies" => {
129
+ "db" => { "revision_id" => "7777777777777777777777777777777777777777" },
130
+ "appserver" => { "revision_id" => "2222222222222222222222222222222222222222" },
131
+ "cache" => { "revision_id" => "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" }
132
+ }
133
+ }
134
+ end
135
+
136
+ let(:prod_group_data) do
137
+ {
138
+ "uri" => api_url("policy_groups/prod"),
139
+ "policies" => {
140
+ "db" => { "revision_id" => "8888888888888888888888888888888888888888" },
141
+ "appserver" => { "revision_id" => "3333333333333333333333333333333333333333" }
142
+ }
143
+ }
144
+ end
145
+
146
+ let(:policy_group_list_data) do
147
+ {
148
+ "dev" => dev_group_data,
149
+ "test" => test_group_data,
150
+ "prod" => prod_group_data
151
+ }
152
+ end
153
+
154
+ let(:expected_policy_list) do
155
+ {
156
+ "appserver" => {
157
+ "1111111111111111111111111111111111111111" => {},
158
+ "2222222222222222222222222222222222222222" => {},
159
+ "3333333333333333333333333333333333333333" => {},
160
+ "4444444444444444444444444444444444444444" => {}
161
+ },
162
+ "db" => {
163
+ "6666666666666666666666666666666666666666" => {},
164
+ "7777777777777777777777777777777777777777" => {},
165
+ "8888888888888888888888888888888888888888" => {},
166
+ "9999999999999999999999999999999999999999" => {}
167
+ },
168
+ "cache" => {
169
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" => {},
170
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" => {}
171
+ }
172
+ }
173
+ end
174
+
175
+ let(:expected_policy_group_list) do
176
+ {
177
+ "dev" => {
178
+ "db" => "6666666666666666666666666666666666666666",
179
+ "appserver" => "1111111111111111111111111111111111111111",
180
+ "cache" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
181
+ },
182
+ "test" => {
183
+ "db" => "7777777777777777777777777777777777777777",
184
+ "appserver" => "2222222222222222222222222222222222222222",
185
+ "cache" => "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
186
+ },
187
+ "prod" => {
188
+ "db" =>"8888888888888888888888888888888888888888",
189
+ "appserver" => "3333333333333333333333333333333333333333"
190
+ }
191
+ }
192
+ end
193
+
194
+
195
+ it "gives a Hash of policy revisions by policy name" do
196
+ expect(info_fetcher.policies_by_name).to eq(expected_policy_list)
197
+ end
198
+
199
+ it "gives a Hash of policy revisions by policy group" do
200
+ expect(info_fetcher.policies_by_group).to eq(expected_policy_group_list)
201
+ end
202
+
203
+ it "is not empty" do
204
+ expect(info_fetcher).to_not be_empty
205
+ end
206
+
207
+ it "lists active revisions" do
208
+ expected_active_revisions = expected_policy_group_list.values.map(&:values).flatten
209
+
210
+ expected_active_revisions_set = Set.new(expected_active_revisions)
211
+ expect(info_fetcher.active_revisions).to eq(expected_active_revisions_set)
212
+ end
213
+
214
+ it "lists orphaned revisions for a given policy" do
215
+ expect(info_fetcher.orphaned_revisions("db")).to eq(%w[ 9999999999999999999999999999999999999999 ])
216
+ expect(info_fetcher.orphaned_revisions("appserver")).to eq(%w[ 4444444444444444444444444444444444444444 ])
217
+ expect(info_fetcher.orphaned_revisions("cache")).to eq([])
218
+ end
219
+
220
+ it "yields revision ids by group" do
221
+ map = {}
222
+
223
+ info_fetcher.revision_ids_by_group_for_each_policy do |policy_name, rev_id_by_group|
224
+ map[policy_name] = rev_id_by_group
225
+ end
226
+
227
+ appserver_rev_ids = {
228
+ "dev" => "1111111111111111111111111111111111111111",
229
+ "test" => "2222222222222222222222222222222222222222",
230
+ "prod" => "3333333333333333333333333333333333333333"
231
+ }
232
+
233
+ expect(map["appserver"].revision_ids_by_group).to eq(appserver_rev_ids)
234
+
235
+ db_rev_ids = {
236
+ "dev" => "6666666666666666666666666666666666666666",
237
+ "test" => "7777777777777777777777777777777777777777",
238
+ "prod" => "8888888888888888888888888888888888888888"
239
+ }
240
+
241
+ expect(map["db"].revision_ids_by_group).to eq(db_rev_ids)
242
+
243
+ cache_rev_ids = {
244
+ "dev" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
245
+ "test" => "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
246
+ "prod" => nil
247
+ }
248
+ expect(map["cache"].revision_ids_by_group).to eq(cache_rev_ids)
249
+ end
250
+
251
+ end
252
+
253
+ end
254
+
255
+ end
256
+