chef-dk 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +2 -2
- data/README.md +25 -0
- data/lib/chef-dk/builtin_commands.rb +4 -0
- data/lib/chef-dk/cli.rb +46 -0
- data/lib/chef-dk/command/base.rb +4 -0
- data/lib/chef-dk/command/generator_commands/template.rb +2 -1
- data/lib/chef-dk/command/install.rb +105 -0
- data/lib/chef-dk/command/push.rb +123 -0
- data/lib/chef-dk/cookbook_profiler/identifiers.rb +5 -0
- data/lib/chef-dk/exceptions.rb +38 -0
- data/lib/chef-dk/generator.rb +16 -1
- data/lib/chef-dk/helpers.rb +1 -1
- data/lib/chef-dk/policyfile/cookbook_location_specification.rb +4 -0
- data/lib/chef-dk/policyfile/cookbook_locks.rb +73 -0
- data/lib/chef-dk/policyfile/reports/install.rb +70 -0
- data/lib/chef-dk/policyfile/reports/table_printer.rb +58 -0
- data/lib/chef-dk/policyfile/reports/upload.rb +70 -0
- data/lib/chef-dk/policyfile/solution_dependencies.rb +102 -8
- data/lib/chef-dk/policyfile/uploader.rb +37 -6
- data/lib/chef-dk/policyfile_compiler.rb +19 -5
- data/lib/chef-dk/policyfile_lock.rb +122 -9
- data/lib/chef-dk/policyfile_services/install.rb +131 -0
- data/lib/chef-dk/policyfile_services/push.rb +121 -0
- data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +6 -4
- data/lib/chef-dk/ui.rb +50 -0
- data/lib/chef-dk/version.rb +1 -1
- data/spec/shared/a_file_generator.rb +4 -1
- data/spec/test_helpers.rb +21 -0
- data/spec/unit/cli_spec.rb +100 -1
- data/spec/unit/command/base_spec.rb +23 -0
- data/spec/unit/command/exec_spec.rb +2 -2
- data/spec/unit/command/install_spec.rb +159 -0
- data/spec/unit/command/push_spec.rb +203 -0
- data/spec/unit/command/shell_init_spec.rb +1 -1
- data/spec/unit/policyfile/cookbook_location_specification_spec.rb +7 -0
- data/spec/unit/policyfile/cookbook_locks_spec.rb +13 -2
- data/spec/unit/policyfile/reports/install_spec.rb +115 -0
- data/spec/unit/policyfile/reports/upload_spec.rb +96 -0
- data/spec/unit/policyfile/solution_dependencies_spec.rb +1 -1
- data/spec/unit/policyfile/uploader_spec.rb +9 -12
- data/spec/unit/policyfile_lock_serialization_spec.rb +292 -0
- data/spec/unit/policyfile_services/install_spec.rb +170 -0
- data/spec/unit/policyfile_services/push_spec.rb +202 -0
- metadata +48 -6
@@ -20,7 +20,7 @@ require 'chef-dk/policyfile/solution_dependencies'
|
|
20
20
|
|
21
21
|
describe ChefDK::Policyfile::SolutionDependencies do
|
22
22
|
|
23
|
-
let(:dependency_data) { {} }
|
23
|
+
let(:dependency_data) { {"Policyfile" => [], "dependencies" => {}} }
|
24
24
|
|
25
25
|
let(:solution_dependencies) do
|
26
26
|
s = described_class.new
|
@@ -79,17 +79,6 @@ describe ChefDK::Policyfile::Uploader do
|
|
79
79
|
expect(uploader.http_client).to eq(http_client)
|
80
80
|
end
|
81
81
|
|
82
|
-
context "when created without an HTTP client" do
|
83
|
-
|
84
|
-
let(:http_client) { nil }
|
85
|
-
|
86
|
-
it "creates an HTTP client with default config" do
|
87
|
-
skip "TODO: determine correct behavior"
|
88
|
-
end
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
82
|
describe "creating uploading documents in compat mode" do
|
94
83
|
|
95
84
|
let(:cookbook_locks) { {} }
|
@@ -132,6 +121,8 @@ describe ChefDK::Policyfile::Uploader do
|
|
132
121
|
|
133
122
|
lock = instance_double("ChefDK::Policyfile::CookbookLock",
|
134
123
|
name: name,
|
124
|
+
version: "1.0.0",
|
125
|
+
identifier: "64b3e64306cff223206348e46af545b19032b170",
|
135
126
|
dotted_decimal_identifier: dotted_decimal_id,
|
136
127
|
cookbook_path: cache_path)
|
137
128
|
|
@@ -212,7 +203,13 @@ describe ChefDK::Policyfile::Uploader do
|
|
212
203
|
it "lists the cookbooks in the lock as possibly needing to be uploaded" do
|
213
204
|
expect(policyfile_lock).to receive(:validate_cookbooks!)
|
214
205
|
|
215
|
-
|
206
|
+
expected_versions_for_policy = cookbook_versions.keys.map do |cb_name|
|
207
|
+
cb = cookbook_versions[cb_name]
|
208
|
+
lock = cookbook_locks[cb_name]
|
209
|
+
ChefDK::Policyfile::Uploader::LockedCookbookForUpload.new(cb, lock)
|
210
|
+
end
|
211
|
+
|
212
|
+
expect(uploader.cookbook_versions_for_policy).to eq(expected_versions_for_policy)
|
216
213
|
end
|
217
214
|
|
218
215
|
it "lists all cookbooks in the lock as needing to be uploaded" do
|
@@ -0,0 +1,292 @@
|
|
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_lock'
|
20
|
+
|
21
|
+
describe ChefDK::PolicyfileLock, "when reading a Policyfile.lock" do
|
22
|
+
|
23
|
+
let(:valid_lock_data) do
|
24
|
+
{
|
25
|
+
"name" => "example",
|
26
|
+
"run_list" => [ "recipe[cookbook::recipe_name]" ],
|
27
|
+
"cookbook_locks" => {
|
28
|
+
# TODO: add some valid locks
|
29
|
+
},
|
30
|
+
"solution_dependencies" => {
|
31
|
+
"Policyfile" => [],
|
32
|
+
"dependencies" => {}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:storage_config) { ChefDK::Policyfile::StorageConfig.new }
|
38
|
+
|
39
|
+
let(:lockfile) { ChefDK::PolicyfileLock.new(storage_config) }
|
40
|
+
|
41
|
+
describe "validating required fields" do
|
42
|
+
|
43
|
+
it "does not raise an error when all fields are valid" do
|
44
|
+
expect { lockfile.build_from_lock_data(valid_lock_data) }.to_not raise_error
|
45
|
+
end
|
46
|
+
|
47
|
+
it "requires the name to be present" do
|
48
|
+
missing_name = valid_lock_data.dup
|
49
|
+
missing_name.delete("name")
|
50
|
+
|
51
|
+
expect { lockfile.build_from_lock_data(missing_name) }.to raise_error(ChefDK::InvalidLockfile)
|
52
|
+
|
53
|
+
blank_name = valid_lock_data.dup
|
54
|
+
blank_name["name"] = ""
|
55
|
+
expect { lockfile.build_from_lock_data(blank_name) }.to raise_error(ChefDK::InvalidLockfile)
|
56
|
+
|
57
|
+
invalid_name = valid_lock_data.dup
|
58
|
+
invalid_name["name"] = {}
|
59
|
+
expect { lockfile.build_from_lock_data(invalid_name) }.to raise_error(ChefDK::InvalidLockfile)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "requires the run_list to be present" do
|
63
|
+
no_run_list = valid_lock_data.dup
|
64
|
+
no_run_list.delete("run_list")
|
65
|
+
|
66
|
+
expect { lockfile.build_from_lock_data(no_run_list) }.to raise_error(ChefDK::InvalidLockfile)
|
67
|
+
|
68
|
+
bad_run_list = valid_lock_data.dup
|
69
|
+
bad_run_list["run_list"] = "bad data"
|
70
|
+
expect { lockfile.build_from_lock_data(bad_run_list) }.to raise_error(ChefDK::InvalidLockfile)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "validates the format of run_list items" do
|
74
|
+
bad_run_list = valid_lock_data.dup
|
75
|
+
bad_run_list["run_list"] = [ "bad data" ]
|
76
|
+
|
77
|
+
expect { lockfile.build_from_lock_data(bad_run_list) }.to raise_error(ChefDK::InvalidLockfile)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "requires the `cookbook_locks` section be present and its value is a Hash" do
|
81
|
+
missing_locks = valid_lock_data.dup
|
82
|
+
missing_locks.delete("cookbook_locks")
|
83
|
+
|
84
|
+
expect { lockfile.build_from_lock_data(missing_locks) }.to raise_error(ChefDK::InvalidLockfile)
|
85
|
+
|
86
|
+
invalid_locks = valid_lock_data.dup
|
87
|
+
invalid_locks["cookbook_locks"] = []
|
88
|
+
expect { lockfile.build_from_lock_data(invalid_locks) }.to raise_error(ChefDK::InvalidLockfile)
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "validating solution_dependencies" do
|
92
|
+
|
93
|
+
it "requires the `solution_dependencies' section be present" do
|
94
|
+
missing_soln_deps = valid_lock_data.dup
|
95
|
+
missing_soln_deps.delete("solution_dependencies")
|
96
|
+
|
97
|
+
expect { lockfile.build_from_lock_data(missing_soln_deps) }.to raise_error(ChefDK::InvalidLockfile)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "requires the solution_dependencies object be a Hash" do
|
101
|
+
invalid_soln_deps = valid_lock_data.dup
|
102
|
+
invalid_soln_deps["solution_dependencies"] = []
|
103
|
+
expect { lockfile.build_from_lock_data(invalid_soln_deps) }.to raise_error(ChefDK::InvalidLockfile)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "requires the solution_dependencies object have a 'Policyfile' and 'dependencies' key" do
|
107
|
+
missing_keys_soln_deps = valid_lock_data.dup
|
108
|
+
missing_keys_soln_deps["solution_dependencies"] = {}
|
109
|
+
expect { lockfile.build_from_lock_data(missing_keys_soln_deps) }.to raise_error(ChefDK::InvalidLockfile)
|
110
|
+
|
111
|
+
missing_policyfile_key = valid_lock_data.dup
|
112
|
+
missing_policyfile_key["solution_dependencies"] = {"dependencies" => {} }
|
113
|
+
expect { lockfile.build_from_lock_data(missing_policyfile_key) }.to raise_error(ChefDK::InvalidLockfile)
|
114
|
+
|
115
|
+
missing_dependencies_key = valid_lock_data.dup
|
116
|
+
missing_dependencies_key["solution_dependencies"] = { "Policyfile" => [] }
|
117
|
+
expect { lockfile.build_from_lock_data(missing_dependencies_key) }.to raise_error(ChefDK::InvalidLockfile)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "requires the Policyfile dependencies be an Array" do
|
121
|
+
invalid_policyfile_deps = valid_lock_data.dup
|
122
|
+
invalid_policyfile_deps["solution_dependencies"] = {"Policyfile" => 42, "dependencies" => {} }
|
123
|
+
expect { lockfile.build_from_lock_data(invalid_policyfile_deps) }.to raise_error(ChefDK::InvalidLockfile)
|
124
|
+
end
|
125
|
+
|
126
|
+
it %q(requires the Policyfile dependencies be formatted like [ "COOKBOOK_NAME", "CONSTRAINT" ]) do
|
127
|
+
invalid_policyfile_deps_content = valid_lock_data.dup
|
128
|
+
invalid_policyfile_deps_content["solution_dependencies"] = { "Policyfile" => [ "bad" ], "dependencies" => {} }
|
129
|
+
expect { lockfile.build_from_lock_data(invalid_policyfile_deps_content) }.to raise_error(ChefDK::InvalidLockfile)
|
130
|
+
|
131
|
+
invalid_policyfile_deps_content2 = valid_lock_data.dup
|
132
|
+
invalid_policyfile_deps_content2["solution_dependencies"] = { "Policyfile" => [ [42, "~> 2.0"] ], "dependencies" => {} }
|
133
|
+
expect { lockfile.build_from_lock_data(invalid_policyfile_deps_content2) }.to raise_error(ChefDK::InvalidLockfile)
|
134
|
+
|
135
|
+
invalid_policyfile_deps_content3 = valid_lock_data.dup
|
136
|
+
invalid_policyfile_deps_content3["solution_dependencies"] = { "Policyfile" => [ ["cookbook_name", "bad"] ], "dependencies" => {} }
|
137
|
+
expect { lockfile.build_from_lock_data(invalid_policyfile_deps_content3) }.to raise_error(ChefDK::InvalidLockfile)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "requires the cookbook dependencies be a Hash" do
|
141
|
+
invalid_cookbook_deps = valid_lock_data.dup
|
142
|
+
invalid_cookbook_deps["solution_dependencies"] = { "Policyfile" => [], "dependencies" => 42 }
|
143
|
+
expect { lockfile.build_from_lock_data(invalid_cookbook_deps) }.to raise_error(ChefDK::InvalidLockfile)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "requires the cookbook dependencies entries be in the correct format" do
|
147
|
+
invalid_cookbook_deps = valid_lock_data.dup
|
148
|
+
bad_deps = { 42 => 42 }
|
149
|
+
invalid_cookbook_deps["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps }
|
150
|
+
expect { lockfile.build_from_lock_data(invalid_cookbook_deps) }.to raise_error(ChefDK::InvalidLockfile)
|
151
|
+
|
152
|
+
invalid_cookbook_deps2 = valid_lock_data.dup
|
153
|
+
bad_deps2 = { "bad-format" => [] }
|
154
|
+
invalid_cookbook_deps2["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps2 }
|
155
|
+
expect { lockfile.build_from_lock_data(invalid_cookbook_deps2) }.to raise_error(ChefDK::InvalidLockfile)
|
156
|
+
|
157
|
+
invalid_cookbook_deps3 = valid_lock_data.dup
|
158
|
+
bad_deps3 = { "cookbook (1.0.0)" => 42 }
|
159
|
+
invalid_cookbook_deps3["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps3 }
|
160
|
+
expect { lockfile.build_from_lock_data(invalid_cookbook_deps3) }.to raise_error(ChefDK::InvalidLockfile)
|
161
|
+
|
162
|
+
invalid_cookbook_deps4 = valid_lock_data.dup
|
163
|
+
bad_deps4 = { "cookbook (1.0.0)" => [ 42 ] }
|
164
|
+
invalid_cookbook_deps4["solution_dependencies"] = { "Policyfile" => [], "dependencies" => bad_deps4 }
|
165
|
+
expect { lockfile.build_from_lock_data(invalid_cookbook_deps4) }.to raise_error(ChefDK::InvalidLockfile)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "validating cookbook_locks entries" do
|
170
|
+
|
171
|
+
# TODO: also check non-cached cookbook
|
172
|
+
let(:valid_cookbook_lock) do
|
173
|
+
{
|
174
|
+
"version" => "1.0.0",
|
175
|
+
"identifier" => "68c13b136a49b4e66cfe9d8aa2b5a85167b5bf9b",
|
176
|
+
"dotted_decimal_identifier" => "111.222.333",
|
177
|
+
"cache_key" => "foo-1.0.0",
|
178
|
+
"source_options" => {}
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
it "requires that each cookbook lock be a Hash" do
|
183
|
+
invalid_cookbook_lock = valid_lock_data.dup
|
184
|
+
invalid_cookbook_lock["cookbook_locks"] = { "foo" => 42 }
|
185
|
+
expect { lockfile.build_from_lock_data(invalid_cookbook_lock) }.to raise_error(ChefDK::InvalidLockfile)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "requires that cookbook locks not be empty" do
|
189
|
+
invalid_cookbook_lock = valid_lock_data.dup
|
190
|
+
invalid_cookbook_lock["cookbook_locks"] = { "foo" => {} }
|
191
|
+
expect { lockfile.build_from_lock_data(invalid_cookbook_lock) }.to raise_error(ChefDK::InvalidLockfile)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "requires that each cookbook lock have a version" do
|
195
|
+
invalid_lockfile = valid_lock_data.dup
|
196
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
197
|
+
invalid_cookbook_lock.delete("version")
|
198
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
199
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "requires that the version be a string" do
|
203
|
+
invalid_lockfile = valid_lock_data.dup
|
204
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
205
|
+
invalid_cookbook_lock["version"] = 42
|
206
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
207
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
208
|
+
end
|
209
|
+
|
210
|
+
it "requires that each cookbook lock have an identifier" do
|
211
|
+
invalid_lockfile = valid_lock_data.dup
|
212
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
213
|
+
invalid_cookbook_lock.delete("identifier")
|
214
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
215
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
216
|
+
end
|
217
|
+
|
218
|
+
it "requires that the identifier be a string" do
|
219
|
+
invalid_lockfile = valid_lock_data.dup
|
220
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
221
|
+
invalid_cookbook_lock["identifier"] = 42
|
222
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
223
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
224
|
+
end
|
225
|
+
|
226
|
+
it "requires that a cookbook lock have a key named `cache_key'" do
|
227
|
+
invalid_lockfile = valid_lock_data.dup
|
228
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
229
|
+
invalid_cookbook_lock.delete("cache_key")
|
230
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
231
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "requires that the cache_key be a string or null" do
|
235
|
+
invalid_lockfile = valid_lock_data.dup
|
236
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
237
|
+
invalid_cookbook_lock["cache_key"] = 42
|
238
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
239
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
240
|
+
end
|
241
|
+
|
242
|
+
it "requires that a cookbook lock have a source_options attribute" do
|
243
|
+
invalid_lockfile = valid_lock_data.dup
|
244
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
245
|
+
invalid_cookbook_lock.delete("source_options")
|
246
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
247
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "requires that source options be a Hash" do
|
251
|
+
invalid_lockfile = valid_lock_data.dup
|
252
|
+
invalid_cookbook_lock = valid_cookbook_lock.dup
|
253
|
+
invalid_cookbook_lock["source_options"] = 42
|
254
|
+
invalid_lockfile["cookbook_locks"] = { "foo" => invalid_cookbook_lock }
|
255
|
+
expect { lockfile.build_from_lock_data(invalid_lockfile) }.to raise_error(ChefDK::InvalidLockfile)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "requires that a cookbook lock be a valid local cookbook if `cache_key' is null/nil" do
|
259
|
+
valid_lock_with_local_cookbook = valid_lock_data.dup
|
260
|
+
valid_local_cookbook = valid_cookbook_lock.dup
|
261
|
+
valid_local_cookbook["cache_key"] = nil
|
262
|
+
valid_local_cookbook["source"] = "path/to/foo"
|
263
|
+
valid_lock_with_local_cookbook["cookbook_locks"] = { "foo" => valid_local_cookbook }
|
264
|
+
expect { lockfile.build_from_lock_data(valid_lock_with_local_cookbook) }.to_not raise_error
|
265
|
+
|
266
|
+
invalid_lock_with_local_cookbook = valid_lock_data.dup
|
267
|
+
invalid_local_cookbook = valid_cookbook_lock.dup
|
268
|
+
invalid_local_cookbook["cache_key"] = nil
|
269
|
+
invalid_local_cookbook["source"] = 42
|
270
|
+
invalid_lock_with_local_cookbook["cookbook_locks"] = { "foo" => invalid_local_cookbook }
|
271
|
+
expect { lockfile.build_from_lock_data(invalid_lock_with_local_cookbook) }.to raise_error(ChefDK::InvalidLockfile)
|
272
|
+
end
|
273
|
+
|
274
|
+
it "requires that a cookbook lock w/ a key named `cache_key' be a valid cached cookbook structure" do
|
275
|
+
valid_lock_with_cached_cookbook = valid_lock_data.dup
|
276
|
+
valid_cached_cookbook = valid_cookbook_lock.dup
|
277
|
+
valid_cached_cookbook["cache_key"] = nil
|
278
|
+
valid_cached_cookbook["source"] = "path/to/foo"
|
279
|
+
valid_lock_with_cached_cookbook["cookbook_locks"] = { "foo" => valid_cached_cookbook }
|
280
|
+
expect { lockfile.build_from_lock_data(valid_lock_with_cached_cookbook) }.to_not raise_error
|
281
|
+
|
282
|
+
invalid_lock_with_cached_cookbook = valid_lock_data.dup
|
283
|
+
invalid_cached_cookbook = valid_cookbook_lock.dup
|
284
|
+
invalid_cached_cookbook["cache_key"] = 42
|
285
|
+
invalid_lock_with_cached_cookbook["cookbook_locks"] = { "foo" => invalid_cached_cookbook }
|
286
|
+
expect { lockfile.build_from_lock_data(invalid_lock_with_cached_cookbook) }.to raise_error(ChefDK::InvalidLockfile)
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
@@ -0,0 +1,170 @@
|
|
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_services/install'
|
20
|
+
|
21
|
+
describe ChefDK::PolicyfileServices::Install do
|
22
|
+
|
23
|
+
let(:working_dir) do
|
24
|
+
path = File.join(tempdir, "policyfile_services_test_working_dir")
|
25
|
+
Dir.mkdir(path)
|
26
|
+
path
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:policyfile_rb_explicit_name) { nil }
|
30
|
+
|
31
|
+
let(:policyfile_rb_name) { policyfile_rb_explicit_name || "Policyfile.rb" }
|
32
|
+
|
33
|
+
let(:policyfile_lock_name) { "Policyfile.lock.json" }
|
34
|
+
|
35
|
+
let(:policyfile_rb_path) { File.join(working_dir, policyfile_rb_name) }
|
36
|
+
|
37
|
+
let(:policyfile_lock_path) { File.join(working_dir, policyfile_lock_name) }
|
38
|
+
|
39
|
+
let(:local_cookbooks_root) do
|
40
|
+
File.join(fixtures_path, "local_path_cookbooks")
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:policyfile_content) do
|
44
|
+
<<-E
|
45
|
+
name 'install-example'
|
46
|
+
|
47
|
+
run_list 'local-cookbook'
|
48
|
+
|
49
|
+
cookbook 'local-cookbook', path: '#{local_cookbooks_root}/local-cookbook'
|
50
|
+
E
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:ui) { TestHelpers::TestUI.new }
|
54
|
+
|
55
|
+
let(:install_service) { described_class.new(policyfile: policyfile_rb_name, ui: ui, root_dir: working_dir) }
|
56
|
+
|
57
|
+
let(:storage_config) do
|
58
|
+
ChefDK::Policyfile::StorageConfig.new( cache_path: nil, relative_paths_root: local_cookbooks_root )
|
59
|
+
end
|
60
|
+
|
61
|
+
def result_policyfile_lock
|
62
|
+
expect(File).to exist(policyfile_lock_path)
|
63
|
+
content = IO.read(policyfile_lock_path)
|
64
|
+
lock_data = FFI_Yajl::Parser.parse(content)
|
65
|
+
ChefDK::PolicyfileLock.new(storage_config).build_from_lock_data(lock_data)
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when no Policyfile is present or specified" do
|
69
|
+
|
70
|
+
it "errors out" do
|
71
|
+
expect { install_service.run }.to raise_error(ChefDK::PolicyfileNotFound, "Policyfile not found at path #{policyfile_rb_path}")
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when a Policyfile exists" do
|
77
|
+
|
78
|
+
before do
|
79
|
+
File.open(policyfile_rb_path, "w+") { |f| f.print(policyfile_content) }
|
80
|
+
end
|
81
|
+
|
82
|
+
it "infers that the Policyfile.rb is located at $CWD/Policyfile.rb" do
|
83
|
+
expect(install_service.policyfile_path).to eq(policyfile_rb_path)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "reads the policyfile from disk" do
|
87
|
+
expect(install_service.policyfile_content).to eq(policyfile_content)
|
88
|
+
end
|
89
|
+
|
90
|
+
context "and the policyfile has an error" do
|
91
|
+
|
92
|
+
let(:policyfile_content) { 'raise "borkbork"' }
|
93
|
+
|
94
|
+
it "errors out and creates no lockfile" do
|
95
|
+
expect { install_service.run }.to raise_error(ChefDK::PolicyfileInstallError)
|
96
|
+
expect(File).to_not exist(policyfile_lock_path)
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
context "and no lockfile exists" do
|
102
|
+
|
103
|
+
it "solves the Policyfile demands, installs cookbooks, emits a lockfile" do
|
104
|
+
install_service.run
|
105
|
+
generated_lock = result_policyfile_lock
|
106
|
+
expect(generated_lock.name).to eq('install-example')
|
107
|
+
expect(generated_lock.cookbook_locks).to have_key("local-cookbook")
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
context "and a lockfile exists" do
|
113
|
+
|
114
|
+
before do
|
115
|
+
install_service.dup.run
|
116
|
+
end
|
117
|
+
|
118
|
+
it "reads the policyfile lock from disk" do
|
119
|
+
lock = install_service.policyfile_lock
|
120
|
+
expect(lock).to be_an_instance_of(ChefDK::PolicyfileLock)
|
121
|
+
expect(lock.name).to eq('install-example')
|
122
|
+
expect(lock.cookbook_locks).to have_key("local-cookbook")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "ensures that cookbooks are installed" do
|
126
|
+
expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_call_original
|
127
|
+
install_service.run
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "when an error occurs during the install" do
|
131
|
+
|
132
|
+
before do
|
133
|
+
expect(install_service.policyfile_lock).to receive(:install_cookbooks).and_raise("some error")
|
134
|
+
end
|
135
|
+
|
136
|
+
it "raises a PolicyfileInstallError" do
|
137
|
+
expect { install_service.run }.to raise_error(ChefDK::PolicyfileInstallError)
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
context "and the Policyfile has updated dependendencies" do
|
144
|
+
|
145
|
+
# For very first iteration, we won't tackle this case if it's hard
|
146
|
+
it "Conservatively updates deps, recomputes lock, and installs"
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
context "and an explicit Policyfile name is given" do
|
153
|
+
|
154
|
+
let(:policyfile_rb_explicit_name) { "MyPolicy.rb" }
|
155
|
+
|
156
|
+
let(:policyfile_lock_name) { "MyPolicy.lock.json" }
|
157
|
+
|
158
|
+
it "infers that the Policyfile.rb is located at $CWD/$POLICYFILE_NAME" do
|
159
|
+
expect(install_service.policyfile_path).to eq(policyfile_rb_path)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "reads the policyfile from disk" do
|
163
|
+
expect(install_service.policyfile_content).to eq(policyfile_content)
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|