chef-dk 0.6.2 → 0.7.0

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