chef-dk 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/chef-dk/authenticated_http.rb +40 -0
  4. data/lib/chef-dk/chef_runner.rb +5 -0
  5. data/lib/chef-dk/command/exec.rb +4 -1
  6. data/lib/chef-dk/command/generate.rb +11 -0
  7. data/lib/chef-dk/command/generator_commands.rb +20 -365
  8. data/lib/chef-dk/command/generator_commands/app.rb +99 -0
  9. data/lib/chef-dk/command/generator_commands/attribute.rb +37 -0
  10. data/lib/chef-dk/command/generator_commands/base.rb +76 -0
  11. data/lib/chef-dk/command/generator_commands/cookbook.rb +100 -0
  12. data/lib/chef-dk/command/generator_commands/cookbook_code_file.rb +99 -0
  13. data/lib/chef-dk/command/generator_commands/cookbook_file.rb +45 -0
  14. data/lib/chef-dk/command/generator_commands/lwrp.rb +36 -0
  15. data/lib/chef-dk/command/generator_commands/recipe.rb +36 -0
  16. data/lib/chef-dk/command/generator_commands/repo.rb +96 -0
  17. data/lib/chef-dk/command/generator_commands/template.rb +45 -0
  18. data/lib/chef-dk/command/verify.rb +28 -0
  19. data/lib/chef-dk/component_test.rb +16 -3
  20. data/lib/chef-dk/cookbook_omnifetch.rb +2 -0
  21. data/lib/chef-dk/cookbook_profiler/identifiers.rb +3 -15
  22. data/lib/chef-dk/exceptions.rb +15 -0
  23. data/lib/chef-dk/generator.rb +102 -25
  24. data/lib/chef-dk/policyfile/community_cookbook_source.rb +0 -7
  25. data/lib/chef-dk/policyfile/{cookbook_spec.rb → cookbook_location_specification.rb} +35 -6
  26. data/lib/chef-dk/policyfile/cookbook_locks.rb +305 -0
  27. data/lib/chef-dk/policyfile/dsl.rb +26 -12
  28. data/lib/chef-dk/policyfile/read_cookbook_for_compat_mode_upload.rb +70 -0
  29. data/lib/chef-dk/policyfile/solution_dependencies.rb +204 -0
  30. data/lib/chef-dk/policyfile/storage_config.rb +77 -0
  31. data/lib/chef-dk/policyfile/uploader.rb +110 -0
  32. data/lib/chef-dk/policyfile_compiler.rb +59 -29
  33. data/lib/chef-dk/policyfile_lock.rb +104 -160
  34. data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +1 -1
  35. data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +0 -1
  36. data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +66 -0
  37. data/lib/chef-dk/skeletons/code_generator/files/default/repo/Rakefile +65 -0
  38. data/lib/chef-dk/skeletons/code_generator/files/default/repo/certificates/README.md +19 -0
  39. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/README-policy.md +9 -0
  40. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/README.md +54 -0
  41. data/lib/chef-dk/skeletons/code_generator/files/default/repo/data_bags/README.md +63 -0
  42. data/lib/chef-dk/skeletons/code_generator/files/default/repo/environments/README.md +5 -0
  43. data/lib/chef-dk/skeletons/code_generator/files/default/repo/roles/README.md +16 -0
  44. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +7 -1
  45. data/lib/chef-dk/skeletons/code_generator/recipes/repo.rb +62 -0
  46. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.all_rights.erb +3 -0
  47. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.apache2.erb +201 -0
  48. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.gplv2.erb +339 -0
  49. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.gplv3.erb +674 -0
  50. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.mit.erb +21 -0
  51. data/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb +1 -4
  52. data/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb +3 -3
  53. data/lib/chef-dk/skeletons/code_generator/templates/default/repo/config/rake.rb.erb +38 -0
  54. data/lib/chef-dk/skeletons/code_generator/templates/default/repo/gitignore.erb +11 -0
  55. data/lib/chef-dk/version.rb +1 -1
  56. data/spec/shared/a_file_generator.rb +121 -0
  57. data/spec/shared/a_generated_file.rb +12 -0
  58. data/spec/shared/fixture_cookbook_checksums.rb +47 -0
  59. data/spec/spec_helper.rb +4 -2
  60. data/spec/unit/chef_runner_spec.rb +12 -5
  61. data/spec/unit/cli_spec.rb +4 -4
  62. data/spec/unit/command/base_spec.rb +1 -1
  63. data/spec/unit/command/exec_spec.rb +37 -27
  64. data/spec/unit/command/generate_spec.rb +3 -3
  65. data/spec/unit/command/generator_commands/app_spec.rb +131 -0
  66. data/spec/unit/command/generator_commands/attribute_spec.rb +32 -0
  67. data/spec/unit/command/generator_commands/cookbook_file_spec.rb +32 -0
  68. data/spec/unit/command/generator_commands/cookbook_spec.rb +205 -0
  69. data/spec/unit/command/generator_commands/lwrp_spec.rb +32 -0
  70. data/spec/unit/command/generator_commands/recipe_spec.rb +32 -0
  71. data/spec/unit/command/generator_commands/repo_spec.rb +287 -0
  72. data/spec/unit/command/generator_commands/template_spec.rb +32 -0
  73. data/spec/unit/command/shell_init_spec.rb +4 -4
  74. data/spec/unit/command/verify_spec.rb +9 -9
  75. data/spec/unit/commands_map_spec.rb +1 -1
  76. data/spec/unit/component_test_spec.rb +3 -3
  77. data/spec/unit/cookbook_profiler/git_spec.rb +7 -7
  78. data/spec/unit/cookbook_profiler/identifiers_spec.rb +12 -8
  79. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/Berksfile +1 -1
  80. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/Berksfile +1 -1
  81. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/Berksfile +1 -1
  82. data/spec/unit/fixtures/example_cookbook/Berksfile +1 -1
  83. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/README.md +4 -0
  84. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/chefignore +96 -0
  85. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/metadata.rb +8 -0
  86. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/recipes/default.rb +8 -0
  87. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/Berksfile +1 -1
  88. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/.kitchen.yml +16 -0
  89. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/README.md +4 -0
  90. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/metadata.rb +8 -0
  91. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/recipes/default.rb +8 -0
  92. data/spec/unit/generator_spec.rb +120 -0
  93. data/spec/unit/policyfile/{cookbook_spec_spec.rb → cookbook_location_specification_spec.rb} +83 -38
  94. data/spec/unit/policyfile/cookbook_locks_spec.rb +354 -0
  95. data/spec/unit/policyfile/read_cookbook_for_compat_mode_upload_spec.rb +85 -0
  96. data/spec/unit/policyfile/solution_dependencies_spec.rb +145 -0
  97. data/spec/unit/policyfile/storage_config_spec.rb +98 -0
  98. data/spec/unit/policyfile/uploader_spec.rb +292 -0
  99. data/spec/unit/policyfile_demands_spec.rb +177 -24
  100. data/spec/unit/policyfile_evaluation_spec.rb +40 -12
  101. data/spec/unit/{policyfile_builder_spec.rb → policyfile_lock_build_spec.rb} +179 -64
  102. data/spec/unit/policyfile_lock_install_spec.rb +138 -0
  103. data/spec/unit/policyfile_lock_validation_spec.rb +610 -0
  104. metadata +103 -59
  105. data/spec/unit/command/generator_commands_spec.rb +0 -504
@@ -0,0 +1,138 @@
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 'shared/fixture_cookbook_checksums'
20
+ require 'chef-dk/policyfile_lock.rb'
21
+
22
+ describe ChefDK::PolicyfileLock, "installing cookbooks from a lockfile" do
23
+
24
+ include_context "fixture cookbooks checksums"
25
+
26
+ let(:cache_path) do
27
+ File.expand_path("spec/unit/fixtures/cookbook_cache", project_root)
28
+ end
29
+
30
+ let(:policyfile_lock_path) { "/fakepath/Policyfile.lock.json" }
31
+
32
+ let(:local_cookbooks_root) { File.join(fixtures_path, "local_path_cookbooks") }
33
+
34
+ let(:name) { "application-server" }
35
+
36
+ let(:run_list) { [ 'recipe[erlang::default]', 'recipe[erchef::prereqs]', 'recipe[erchef::app]' ] }
37
+
38
+ let(:storage_config) do
39
+ ChefDK::Policyfile::StorageConfig.new( cache_path: cache_path, relative_paths_root: local_cookbooks_root )
40
+ end
41
+
42
+ let(:lock_generator) do
43
+ ChefDK::PolicyfileLock.build(storage_config) do |policy|
44
+
45
+ policy.name = name
46
+
47
+ policy.run_list = run_list
48
+
49
+ policy.cached_cookbook("foo") do |c|
50
+ c.origin = "https://artifact-server.example/foo/1.0.0"
51
+ c.cache_key = "foo-1.0.0"
52
+ c.source_options = { artifactserver: "https://artifact-server.example/foo/1.0.0", version: "1.0.0" }
53
+ end
54
+
55
+ policy.local_cookbook("local-cookbook") do |c|
56
+ c.source = "local-cookbook"
57
+ c.source_options = { path: "local-cookbook" }
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+ let(:lock_data) do
64
+ lock_generator.to_lock
65
+ end
66
+
67
+ let(:policyfile_lock) do
68
+ ChefDK::PolicyfileLock.new(storage_config).build_from_lock_data(lock_data)
69
+ end
70
+
71
+ describe "Populating a PolicyfileLock from a lockfile data structure" do
72
+
73
+ it "imports the name attribute" do
74
+ expect(policyfile_lock.name).to eq(name)
75
+ end
76
+
77
+ it "imports the run_list attribute" do
78
+ expect(policyfile_lock.run_list).to eq(run_list)
79
+ end
80
+
81
+ it "imports cached cookbook lock data" do
82
+ expect(policyfile_lock.cookbook_locks).to have_key("foo")
83
+ cookbook_lock = policyfile_lock.cookbook_locks["foo"]
84
+ expect(cookbook_lock.name).to eq("foo")
85
+ expect(cookbook_lock.cache_key).to eq("foo-1.0.0")
86
+ expect(cookbook_lock.version).to eq("1.0.0")
87
+ expect(cookbook_lock.identifier).to eq(cookbook_foo_cksum)
88
+ expect(cookbook_lock.dotted_decimal_identifier).to eq(cookbook_foo_cksum_dotted)
89
+ expect(cookbook_lock.origin).to eq("https://artifact-server.example/foo/1.0.0")
90
+ expect(cookbook_lock.source_options).to eq({ artifactserver: "https://artifact-server.example/foo/1.0.0", version: "1.0.0" })
91
+ expect(cookbook_lock.cookbook_location_spec.version_constraint).to eq(Semverse::Constraint.new("= 1.0.0"))
92
+ end
93
+
94
+ it "imports local cookbook lock data" do
95
+ expect(policyfile_lock.cookbook_locks).to have_key("local-cookbook")
96
+ cookbook_lock = policyfile_lock.cookbook_locks["local-cookbook"]
97
+ expect(cookbook_lock.name).to eq("local-cookbook")
98
+ expect(cookbook_lock.version).to eq("2.3.4")
99
+ expect(cookbook_lock.identifier).to eq("fab501cfaf747901bd82c1bc706beae7dc3a350c")
100
+ expect(cookbook_lock.dotted_decimal_identifier).to eq("70567763561641081.489844270461035.258281553147148")
101
+ expect(cookbook_lock.source).to eq("local-cookbook")
102
+ expect(cookbook_lock.source_options).to eq({ path: "local-cookbook" })
103
+ expect(cookbook_lock.cookbook_location_spec.version_constraint).to eq(Semverse::Constraint.new("= 2.3.4"))
104
+ end
105
+
106
+ end
107
+
108
+ describe "installing cookbooks" do
109
+
110
+ let(:remote_cookbook_lock) { policyfile_lock.cookbook_locks["foo"] }
111
+
112
+ let(:local_cookbook_lock) { policyfile_lock.cookbook_locks["local-cookbook"] }
113
+
114
+ it "configures the cookbook location spec for a remote cookbook" do
115
+ location_spec = remote_cookbook_lock.cookbook_location_spec
116
+ expect(location_spec).to be_an_instance_of(ChefDK::Policyfile::CookbookLocationSpecification)
117
+ expect(location_spec.uri).to eq("https://artifact-server.example/foo/1.0.0")
118
+ expect(location_spec.source_options[:version]).to eq("1.0.0")
119
+ end
120
+
121
+ it "configures the installer for a local cookbook" do
122
+ location_spec = local_cookbook_lock.cookbook_location_spec
123
+ expect(location_spec).to be_an_instance_of(ChefDK::Policyfile::CookbookLocationSpecification)
124
+
125
+ expect(location_spec.relative_path).to eq('local-cookbook')
126
+ end
127
+
128
+
129
+ it "ensures the cookbooks are installed" do
130
+ expect(remote_cookbook_lock.cookbook_location_spec).to receive(:ensure_cached)
131
+ expect(local_cookbook_lock.cookbook_location_spec).to receive(:ensure_cached)
132
+
133
+ policyfile_lock.install_cookbooks
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,610 @@
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.rb'
20
+
21
+ describe ChefDK::PolicyfileLock, "validating locked cookbooks" do
22
+
23
+ let(:pristine_cache_path) do
24
+ File.expand_path("spec/unit/fixtures/cookbook_cache", project_root)
25
+ end
26
+
27
+ let(:cache_path) do
28
+ temp_cache_path = File.join(tempdir, "local_cache")
29
+ FileUtils.cp_r(pristine_cache_path, temp_cache_path)
30
+ temp_cache_path
31
+ end
32
+
33
+ let(:policyfile_lock_path) { "/fakepath/Policyfile.lock.json" }
34
+
35
+ let(:local_cookbooks_root) do
36
+ temp_local_path_cookbooks = File.join(tempdir, "local_path_cookbooks")
37
+ FileUtils.cp_r(File.join(fixtures_path, "local_path_cookbooks"), temp_local_path_cookbooks)
38
+ temp_local_path_cookbooks
39
+ end
40
+
41
+ let(:name) { "application-server" }
42
+
43
+ let(:run_list) { [ 'recipe[erlang::default]', 'recipe[erchef::prereqs]', 'recipe[erchef::app]' ] }
44
+
45
+ let(:storage_config) do
46
+ ChefDK::Policyfile::StorageConfig.new( cache_path: cache_path, relative_paths_root: local_cookbooks_root )
47
+ end
48
+
49
+ let(:solution_dependencies) do
50
+ {
51
+ "Policyfile" => [],
52
+ "dependencies" => {
53
+ "foo (1.0.0)" => [],
54
+ "local-cookbook (2.3.4)" => []
55
+ }
56
+ }
57
+ end
58
+
59
+ let(:lock_generator) do
60
+ ChefDK::PolicyfileLock.build(storage_config) do |policy|
61
+
62
+ policy.name = name
63
+
64
+ policy.run_list = run_list
65
+
66
+ policy.cached_cookbook("foo") do |c|
67
+ c.origin = "https://artifact-server.example/foo/1.0.0"
68
+ c.cache_key = "foo-1.0.0"
69
+ c.source_options = { artifactserver: "https://artifact-server.example/foo/1.0.0", version: "1.0.0" }
70
+ end
71
+
72
+ policy.local_cookbook("local-cookbook") do |c|
73
+ c.source = "local-cookbook"
74
+ c.source_options = { path: "local-cookbook" }
75
+ end
76
+
77
+ policy.solution_dependencies.consume_lock_data(solution_dependencies)
78
+ end
79
+ end
80
+
81
+ let(:lock_data) do
82
+ lock_generator.to_lock
83
+ end
84
+
85
+ # Eagerly evaluate #policyfile_lock. This is necessary because many of
86
+ # the tests follow this general process:
87
+ # 1. setup valid state, generate a lockfile in-memory
88
+ # 2. make the state invalid
89
+ # 3. ensure validation detected the invalid state
90
+ #
91
+ # With lazy evaluation, #1 may happen after #2.
92
+ let!(:policyfile_lock) do
93
+ ChefDK::PolicyfileLock.new(storage_config).build_from_lock_data(lock_data)
94
+ end
95
+
96
+ let(:local_cookbook_path) { File.join(local_cookbooks_root, "local-cookbook") }
97
+
98
+ context "when no cookbooks have changed" do
99
+
100
+ it "validation succeeds" do
101
+ expect(policyfile_lock.validate_cookbooks!).to be true
102
+ end
103
+
104
+ end
105
+
106
+ describe "when a :path sourced cookbook has changed" do
107
+
108
+ let(:metadata_path) { File.join(local_cookbook_path, "metadata.rb") }
109
+
110
+ let(:cookbook_lock_data) { policyfile_lock.lock_data_for("local-cookbook") }
111
+
112
+ # Validate the metadata is correct, so we know the test setup code
113
+ # hasn't done something wrong.
114
+ def ensure_metadata_as_expected!
115
+ expected_metadata_rb=<<-E
116
+ name 'local-cookbook'
117
+ maintainer ''
118
+ maintainer_email ''
119
+ license ''
120
+ description 'Installs/Configures local-cookbook'
121
+ long_description 'Installs/Configures local-cookbook'
122
+ version '2.3.4'
123
+
124
+ E
125
+ expect(IO.read(metadata_path)).to eq(expected_metadata_rb)
126
+ end
127
+
128
+ context "when the cookbook is missing" do
129
+
130
+ before do
131
+ FileUtils.rm_rf(local_cookbook_path)
132
+ end
133
+
134
+ it "reports the missing cookbook and fails validation" do
135
+ full_path = File.expand_path(local_cookbook_path)
136
+ message = "Cookbook `local-cookbook' not found at path source `local-cookbook` (full path: `#{full_path}')"
137
+
138
+ expect { policyfile_lock.validate_cookbooks! }.to raise_error(ChefDK::LocalCookbookNotFound, message)
139
+ end
140
+
141
+ end
142
+
143
+ context "when the cookbook has an incorrect name" do
144
+
145
+ let(:new_metadata) do
146
+ <<-E
147
+ name 'WRONG'
148
+ maintainer ''
149
+ maintainer_email ''
150
+ license ''
151
+ description 'Installs/Configures local-cookbook'
152
+ long_description 'Installs/Configures local-cookbook'
153
+ version '2.3.4'
154
+
155
+ E
156
+ end
157
+
158
+
159
+ before do
160
+ ensure_metadata_as_expected!
161
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
162
+ end
163
+
164
+ it "reports the unexpected cookbook and fails validation" do
165
+ policyfile_lock
166
+
167
+ message = "The cookbook at path source `local-cookbook' is expected to be named `local-cookbook', but is now named `WRONG' (full path: #{local_cookbook_path})"
168
+ expect { policyfile_lock.validate_cookbooks! }.to raise_error(ChefDK::MalformedCookbook, message)
169
+ end
170
+
171
+ end
172
+
173
+ context "when the cookbook has an updated version that violates no dependency constraints" do
174
+
175
+ let(:new_metadata) do
176
+ <<-E
177
+ name 'local-cookbook'
178
+ maintainer ''
179
+ maintainer_email ''
180
+ license ''
181
+ description 'Installs/Configures local-cookbook'
182
+ long_description 'Installs/Configures local-cookbook'
183
+ version '2.3.5' # changed from 2.3.4
184
+
185
+ E
186
+ end
187
+
188
+ before do
189
+ ensure_metadata_as_expected!
190
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
191
+ policyfile_lock.validate_cookbooks! # no error
192
+ end
193
+
194
+ it "updates the version information in the lockfile" do
195
+ expect(cookbook_lock_data.version).to eq("2.3.5")
196
+ end
197
+
198
+ it "updates the content identifier" do
199
+ old_id = lock_generator.lock_data_for("local-cookbook").identifier
200
+
201
+ expect(cookbook_lock_data.identifier).to_not eq(old_id)
202
+ expect(cookbook_lock_data.identifier).to eq("d71622904ed89b1e0066bb4ae823b2a7b49a615a")
203
+ end
204
+
205
+ it "has an updated version and identifier" do
206
+ expect(cookbook_lock_data).to be_updated
207
+ expect(cookbook_lock_data.version_updated?).to be true
208
+ expect(cookbook_lock_data.identifier_updated?).to be true
209
+ end
210
+ end
211
+
212
+ context "when the cookbook has an updated version that violates other dependency constraints" do
213
+
214
+ let(:solution_dependencies) do
215
+ {
216
+ "Policyfile" => [],
217
+ "dependencies" => {
218
+ "foo (1.0.0)" => [ [ "local-cookbook", "~> 2.0" ] ]
219
+ }
220
+ }
221
+ end
222
+
223
+ let(:new_metadata) do
224
+ <<-E
225
+ name 'local-cookbook'
226
+ maintainer ''
227
+ maintainer_email ''
228
+ license ''
229
+ description 'Installs/Configures local-cookbook'
230
+ long_description 'Installs/Configures local-cookbook'
231
+ version '3.0.0' # changed from 2.3.4, violates `~> 2.0` constraint
232
+
233
+ E
234
+ end
235
+
236
+ before do
237
+ ensure_metadata_as_expected!
238
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
239
+ end
240
+
241
+ it "reports the dependency conflict and fails validation" do
242
+ expected_message = "Cookbook local-cookbook (3.0.0) conflicts with other dependencies:\nfoo (1.0.0) depends on local-cookbook ~> 2.0"
243
+ expect { policyfile_lock.validate_cookbooks! }.to raise_error(ChefDK::DependencyConflict, expected_message)
244
+ end
245
+
246
+ end
247
+
248
+ context "when a :path sourced cookbook has updated content" do
249
+
250
+ let(:recipe_path) { File.join(local_cookbook_path, "recipes/default.rb") }
251
+
252
+ let(:new_recipe) do
253
+ <<-E
254
+ # This is totally new code,
255
+ # it is different than the old code
256
+
257
+ E
258
+ end
259
+
260
+ before do
261
+ ensure_metadata_as_expected!
262
+ File.open(recipe_path, "w+") { |f| f.print(new_recipe) }
263
+ policyfile_lock.validate_cookbooks! # no error
264
+ end
265
+
266
+ it "updates the lockfile with the new checksum and validation succeeds" do
267
+ old_id = lock_generator.lock_data_for("local-cookbook").identifier
268
+
269
+ expect(cookbook_lock_data.identifier).to_not eq(old_id)
270
+ expect(cookbook_lock_data.identifier).to eq("08a96e3afbd1eaa1183a2dde8687ca29dbddc94b")
271
+ end
272
+
273
+ it "has an updated identifier but not an updated version" do
274
+ expect(cookbook_lock_data).to be_updated
275
+ expect(cookbook_lock_data.version_updated?).to be false
276
+ expect(cookbook_lock_data.identifier_updated?).to be true
277
+ end
278
+
279
+ end
280
+
281
+ context "when a :path source cookbook has added a dependency satisfied by the current cookbook set" do
282
+
283
+ let(:new_metadata) do
284
+ <<-E
285
+ name 'local-cookbook'
286
+ maintainer ''
287
+ maintainer_email ''
288
+ license ''
289
+ description 'Installs/Configures local-cookbook'
290
+ long_description 'Installs/Configures local-cookbook'
291
+ version '2.3.4'
292
+
293
+ depends "foo", "=1.0.0"
294
+
295
+ E
296
+ end
297
+
298
+ before do
299
+ ensure_metadata_as_expected!
300
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
301
+ policyfile_lock.validate_cookbooks! # no error
302
+ end
303
+
304
+ it "updates the lockfile with the new checksum and validation succeeds" do
305
+ old_id = lock_generator.lock_data_for("local-cookbook").identifier
306
+
307
+ expect(cookbook_lock_data.identifier).to_not eq(old_id)
308
+ expect(cookbook_lock_data.identifier).to eq("da6a1c0f8791df713b7ff8c27285fbe7923901cc")
309
+ end
310
+
311
+ it "has an updated identifier but not an updated version" do
312
+ expect(cookbook_lock_data).to be_updated
313
+ expect(cookbook_lock_data.version_updated?).to be false
314
+ expect(cookbook_lock_data.identifier_updated?).to be true
315
+ end
316
+
317
+ it "has an updated dependency set" do
318
+ actual = policyfile_lock.solution_dependencies.to_lock["dependencies"]
319
+ expected = {
320
+ "local-cookbook (2.3.4)" => [ ["foo", "= 1.0.0"] ],
321
+ "foo (1.0.0)" => []
322
+ }
323
+ expect(actual).to eq(expected)
324
+ end
325
+
326
+ end
327
+
328
+ context "when a :path source cookbook has added a dependency not satisfied by the current cookbook set" do
329
+
330
+ let(:new_metadata) do
331
+ <<-E
332
+ name 'local-cookbook'
333
+ maintainer ''
334
+ maintainer_email ''
335
+ license ''
336
+ description 'Installs/Configures local-cookbook'
337
+ long_description 'Installs/Configures local-cookbook'
338
+ version '2.3.4'
339
+
340
+ depends "not-a-thing"
341
+
342
+ E
343
+ end
344
+
345
+ before do
346
+ ensure_metadata_as_expected!
347
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
348
+ end
349
+
350
+ it "reports the not-satisfied dependency and validation fails" do
351
+ error_message = "Cookbook local-cookbook (2.3.4) has dependency constraints that cannot be met by the existing cookbook set:\n" +
352
+ "Cookbook not-a-thing isn't included in the existing cookbook set."
353
+ expect { policyfile_lock.validate_cookbooks! }.to raise_error(ChefDK::DependencyConflict, error_message)
354
+ end
355
+
356
+ end
357
+
358
+ context "when a :path source cookbook has modified a dep constraint and the new constraint is satisfied" do
359
+
360
+ let(:solution_dependencies) do
361
+ {
362
+ "Policyfile" => [],
363
+ "dependencies" => {
364
+ "foo (1.0.0)" => [],
365
+ "local-cookbook (2.3.4)" => [ ["foo", ">= 0.0.0"] ]
366
+ }
367
+ }
368
+ end
369
+
370
+ let(:new_metadata) do
371
+ <<-E
372
+ name 'local-cookbook'
373
+ maintainer ''
374
+ maintainer_email ''
375
+ license ''
376
+ description 'Installs/Configures local-cookbook'
377
+ long_description 'Installs/Configures local-cookbook'
378
+ version '2.3.4'
379
+
380
+ depends "foo", ">= 1.0.0"
381
+
382
+ E
383
+ end
384
+
385
+ before do
386
+ ensure_metadata_as_expected!
387
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
388
+ policyfile_lock.validate_cookbooks! # no error
389
+ end
390
+
391
+ it "updates the lockfile with the new checksum and validation succeeds" do
392
+ actual = policyfile_lock.solution_dependencies.to_lock["dependencies"]
393
+ expected = {
394
+ "local-cookbook (2.3.4)" => [ ["foo", ">= 1.0.0"] ],
395
+ "foo (1.0.0)" => []
396
+ }
397
+ expect(actual).to eq(expected)
398
+ end
399
+
400
+ end
401
+
402
+ context "when a :path source cookbook has modified a dep constraint and the new constraint is not satisfied" do
403
+
404
+ let(:new_metadata) do
405
+ <<-E
406
+ name 'local-cookbook'
407
+ maintainer ''
408
+ maintainer_email ''
409
+ license ''
410
+ description 'Installs/Configures local-cookbook'
411
+ long_description 'Installs/Configures local-cookbook'
412
+ version '2.3.4'
413
+
414
+ depends "foo", "~> 2.0"
415
+
416
+ E
417
+ end
418
+
419
+ before do
420
+ ensure_metadata_as_expected!
421
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
422
+ end
423
+
424
+ it "reports the not-satisfied dependency and validation fails" do
425
+ error_message = "Cookbook local-cookbook (2.3.4) has dependency constraints that cannot be met by the existing cookbook set:\n" +
426
+ "Dependency on foo ~> 2.0 conflicts with existing version foo (1.0.0)"
427
+ expect { policyfile_lock.validate_cookbooks! }.to raise_error(ChefDK::DependencyConflict, error_message)
428
+ end
429
+
430
+
431
+ end
432
+
433
+ context "when a :path source cookbook has updated it's version and another path source cookbook has updated its constraint" do
434
+
435
+ # The situation we want to test here is when the user modifies a
436
+ # cookbook's version, and also the dependency constraint on that cookbook
437
+ # in a different cookbook. For example, cookbook A depends on B ~> 1.0,
438
+ # then the user updates A to depend on B ~> 2.0, and bumps the version of B to 2.0.
439
+
440
+ let(:lock_generator) do
441
+ ChefDK::PolicyfileLock.build(storage_config) do |policy|
442
+
443
+ policy.name = name
444
+
445
+ policy.run_list = run_list
446
+
447
+ policy.cached_cookbook("foo") do |c|
448
+ c.origin = "https://artifact-server.example/foo/1.0.0"
449
+ c.cache_key = "foo-1.0.0"
450
+ c.source_options = { artifactserver: "https://artifact-server.example/foo/1.0.0", version: "1.0.0" }
451
+ end
452
+
453
+ policy.local_cookbook("local-cookbook") do |c|
454
+ c.source = "local-cookbook"
455
+ c.source_options = { path: "local-cookbook" }
456
+ end
457
+
458
+ policy.local_cookbook("another-local-cookbook") do |c|
459
+ c.source = "another-local-cookbook"
460
+ c.source_options = { path: "another-local-cookbook" }
461
+ end
462
+ policy.solution_dependencies.consume_lock_data(solution_dependencies)
463
+ end
464
+ end
465
+
466
+ # Represents dependencies before modification
467
+ let(:solution_dependencies) do
468
+ {
469
+ "Policyfile" => [],
470
+ "dependencies" => {
471
+ "foo (1.0.0)" => [],
472
+ "local-cookbook (2.3.4)" => [ ],
473
+ "another-local-cookbook (0.1.0)" => [ ["local-cookbook", "= 2.3.4"] ]
474
+ }
475
+ }
476
+ end
477
+
478
+ let(:new_metadata_local_cookbook) do
479
+ <<-E
480
+ name 'local-cookbook'
481
+ maintainer ''
482
+ maintainer_email ''
483
+ license ''
484
+ description 'Installs/Configures local-cookbook'
485
+ long_description 'Installs/Configures local-cookbook'
486
+ version '3.0.0' # changed from 2.3.4
487
+
488
+ E
489
+ end
490
+
491
+ let(:new_metadata_another_local_cookbook) do
492
+ <<-E
493
+ name 'another-local-cookbook'
494
+ maintainer ''
495
+ maintainer_email ''
496
+ license ''
497
+ description 'Installs/Configures another-local-cookbook'
498
+ long_description 'Installs/Configures another-local-cookbook'
499
+ version '0.1.0'
500
+
501
+ # This dep now requires the updated version of 'local-cookbook'
502
+ depends 'local-cookbook', '= 3.0.0'
503
+ E
504
+ end
505
+
506
+ let(:metadata_path_another_local_cookbook) do
507
+ File.join(local_cookbooks_root, 'another-local-cookbook', 'metadata.rb')
508
+ end
509
+
510
+ before do
511
+ ensure_metadata_as_expected!
512
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata_local_cookbook) }
513
+ File.open(metadata_path_another_local_cookbook, "w+") { |f| f.print(new_metadata_another_local_cookbook) }
514
+ policyfile_lock.validate_cookbooks! # no error
515
+ end
516
+
517
+ context "and the new constraint is satisfied by they new version" do
518
+
519
+ it "updates the version and constraint in the lockfile (validation succeeds)" do
520
+ actual = policyfile_lock.solution_dependencies.to_lock["dependencies"]
521
+ expected = {
522
+ "local-cookbook (3.0.0)" => [ ],
523
+ "another-local-cookbook (0.1.0)" => [ [ "local-cookbook", "= 3.0.0" ] ],
524
+ "foo (1.0.0)" => []
525
+ }
526
+ expect(actual).to eq(expected)
527
+ end
528
+
529
+ end
530
+
531
+ end
532
+
533
+ end
534
+
535
+ # Cached cookbook is both supermarket and git
536
+ context "when a cached cookbook is modified" do
537
+
538
+ let(:cached_cookbook_path) { File.join(cache_path, "foo-1.0.0") }
539
+
540
+ let(:metadata_path) { File.join(cached_cookbook_path, "metadata.rb") }
541
+
542
+ # Validate the metadata is correct, so we know the test setup code
543
+ # hasn't done something wrong.
544
+ def ensure_metadata_as_expected!
545
+ expected_metadata_rb=<<-E
546
+ name 'foo'
547
+ maintainer ''
548
+ maintainer_email ''
549
+ license ''
550
+ description 'Installs/Configures foo'
551
+ long_description 'Installs/Configures foo'
552
+ version '1.0.0'
553
+
554
+ E
555
+ expect(IO.read(metadata_path)).to eq(expected_metadata_rb)
556
+ end
557
+
558
+ context "when the cookbook missing" do
559
+
560
+ before do
561
+ ensure_metadata_as_expected!
562
+ FileUtils.rm_rf(cached_cookbook_path)
563
+ end
564
+
565
+ it "reports the missing cookbook and fails validation" do
566
+ message = "Cookbook `foo' not found at expected cache location `foo-1.0.0' (full path: `#{cached_cookbook_path}')"
567
+ expect { policyfile_lock.validate_cookbooks! }.to raise_error(ChefDK::CachedCookbookNotFound, message)
568
+ end
569
+
570
+ end
571
+
572
+ context "when the content has changed" do
573
+
574
+ # This basically means the user modified the cached cookbook. There's no
575
+ # technical reason we need to be whiny about this, but if we treat it like
576
+ # we would a path cookbook, you could end up with two cookbooks that look
577
+ # like the canonical (e.g.) apache2 1.2.3 cookbook from supermarket with no
578
+ # indication of which is which.
579
+ #
580
+ # We'll treat it like an error, but we need to provide a "pristine"
581
+ # function to let the user recover.
582
+
583
+ let(:new_metadata) do
584
+ <<-E
585
+ # This is a cached copy of an upstream cookbook, I should not be editing it but
586
+ # YOLO
587
+ name 'foo'
588
+ maintainer ''
589
+ maintainer_email ''
590
+ license ''
591
+ description 'Installs/Configures foo'
592
+ long_description 'Installs/Configures foo'
593
+ version '1.0.0'
594
+ E
595
+ end
596
+
597
+ before do
598
+ ensure_metadata_as_expected!
599
+ policyfile_lock
600
+ File.open(metadata_path, "w+") { |f| f.print(new_metadata) }
601
+ end
602
+
603
+ it "reports the modified cached cookbook and validation fails" do
604
+ message = "Cached cookbook `foo' (1.0.0) has been modified since the lockfile was generated. Cached cookbooks cannot be modified. (full path: `#{cached_cookbook_path}')"
605
+ expect { policyfile_lock.validate_cookbooks! }.to raise_error(ChefDK::CachedCookbookModified, message)
606
+ end
607
+ end
608
+ end
609
+ end
610
+