chef-dk 0.2.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 (142) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +192 -0
  3. data/LICENSE +201 -0
  4. data/README.md +198 -0
  5. data/Rakefile +18 -0
  6. data/bin/chef +25 -0
  7. data/lib/chef-dk.rb +19 -0
  8. data/lib/chef-dk/builtin_commands.rb +31 -0
  9. data/lib/chef-dk/chef_runner.rb +83 -0
  10. data/lib/chef-dk/cli.rb +135 -0
  11. data/lib/chef-dk/command/base.rb +71 -0
  12. data/lib/chef-dk/command/exec.rb +33 -0
  13. data/lib/chef-dk/command/gem.rb +47 -0
  14. data/lib/chef-dk/command/generate.rb +97 -0
  15. data/lib/chef-dk/command/generator_commands.rb +417 -0
  16. data/lib/chef-dk/command/shell_init.rb +80 -0
  17. data/lib/chef-dk/command/verify.rb +226 -0
  18. data/lib/chef-dk/commands_map.rb +115 -0
  19. data/lib/chef-dk/component_test.rb +142 -0
  20. data/lib/chef-dk/cookbook_metadata.rb +36 -0
  21. data/lib/chef-dk/cookbook_omnifetch.rb +29 -0
  22. data/lib/chef-dk/cookbook_profiler/git.rb +95 -0
  23. data/lib/chef-dk/cookbook_profiler/identifiers.rb +79 -0
  24. data/lib/chef-dk/cookbook_profiler/null_scm.rb +32 -0
  25. data/lib/chef-dk/exceptions.rb +46 -0
  26. data/lib/chef-dk/generator.rb +70 -0
  27. data/lib/chef-dk/helpers.rb +95 -0
  28. data/lib/chef-dk/policyfile/chef_server_cookbook_source.rb +46 -0
  29. data/lib/chef-dk/policyfile/community_cookbook_source.rb +84 -0
  30. data/lib/chef-dk/policyfile/cookbook_sources.rb +20 -0
  31. data/lib/chef-dk/policyfile/cookbook_spec.rb +96 -0
  32. data/lib/chef-dk/policyfile/dsl.rb +148 -0
  33. data/lib/chef-dk/policyfile/null_cookbook_source.rb +37 -0
  34. data/lib/chef-dk/policyfile_compiler.rb +217 -0
  35. data/lib/chef-dk/policyfile_lock.rb +243 -0
  36. data/lib/chef-dk/shell_out.rb +36 -0
  37. data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +3 -0
  38. data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +96 -0
  39. data/lib/chef-dk/skeletons/code_generator/files/default/converge_spec.rb +9 -0
  40. data/lib/chef-dk/skeletons/code_generator/files/default/gitignore +16 -0
  41. data/lib/chef-dk/skeletons/code_generator/files/default/spec_helper.rb +8 -0
  42. data/lib/chef-dk/skeletons/code_generator/metadata.rb +8 -0
  43. data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +65 -0
  44. data/lib/chef-dk/skeletons/code_generator/recipes/attribute.rb +12 -0
  45. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +50 -0
  46. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook_file.rb +24 -0
  47. data/lib/chef-dk/skeletons/code_generator/recipes/lwrp.rb +23 -0
  48. data/lib/chef-dk/skeletons/code_generator/recipes/recipe.rb +9 -0
  49. data/lib/chef-dk/skeletons/code_generator/recipes/template.rb +32 -0
  50. data/lib/chef-dk/skeletons/code_generator/templates/default/README.md.erb +4 -0
  51. data/lib/chef-dk/skeletons/code_generator/templates/default/attribute.rb.erb +0 -0
  52. data/lib/chef-dk/skeletons/code_generator/templates/default/cookbook_file.erb +0 -0
  53. data/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb +8 -0
  54. data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen.yml.erb +16 -0
  55. data/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb +8 -0
  56. data/lib/chef-dk/skeletons/code_generator/templates/default/provider.rb.erb +0 -0
  57. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe.rb.erb +0 -0
  58. data/lib/chef-dk/skeletons/code_generator/templates/default/resource.rb.erb +0 -0
  59. data/lib/chef-dk/skeletons/code_generator/templates/default/template.erb +0 -0
  60. data/lib/chef-dk/version.rb +20 -0
  61. data/spec/shared/setup_git_cookbooks.rb +53 -0
  62. data/spec/spec_helper.rb +32 -0
  63. data/spec/test_helpers.rb +59 -0
  64. data/spec/unit/chef_runner_spec.rb +70 -0
  65. data/spec/unit/cli_spec.rb +151 -0
  66. data/spec/unit/command/base_spec.rb +88 -0
  67. data/spec/unit/command/exec_spec.rb +123 -0
  68. data/spec/unit/command/generate_spec.rb +102 -0
  69. data/spec/unit/command/generator_commands_spec.rb +504 -0
  70. data/spec/unit/command/shell_init_spec.rb +109 -0
  71. data/spec/unit/command/verify_spec.rb +311 -0
  72. data/spec/unit/commands_map_spec.rb +57 -0
  73. data/spec/unit/component_test_spec.rb +126 -0
  74. data/spec/unit/cookbook_metadata_spec.rb +62 -0
  75. data/spec/unit/cookbook_profiler/git_spec.rb +127 -0
  76. data/spec/unit/cookbook_profiler/identifiers_spec.rb +79 -0
  77. data/spec/unit/fixtures/chef-runner-cookbooks/test_cookbook/recipes/recipe_one.rb +9 -0
  78. data/spec/unit/fixtures/chef-runner-cookbooks/test_cookbook/recipes/recipe_two.rb +9 -0
  79. data/spec/unit/fixtures/command/cli_test_command.rb +26 -0
  80. data/spec/unit/fixtures/command/explicit_path_example.rb +7 -0
  81. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/.kitchen.yml +16 -0
  82. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/Berksfile +3 -0
  83. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/README.md +4 -0
  84. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/chefignore +96 -0
  85. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/metadata.rb +8 -0
  86. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/recipes/default.rb +8 -0
  87. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/.kitchen.yml +16 -0
  88. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/Berksfile +3 -0
  89. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/README.md +4 -0
  90. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/chefignore +96 -0
  91. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/metadata.rb +8 -0
  92. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/recipes/default.rb +8 -0
  93. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/.kitchen.yml +16 -0
  94. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/Berksfile +3 -0
  95. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/README.md +4 -0
  96. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/chefignore +96 -0
  97. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/metadata.rb +8 -0
  98. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/recipes/default.rb +8 -0
  99. data/spec/unit/fixtures/cookbooks_api/pruned_small_universe.json +1322 -0
  100. data/spec/unit/fixtures/cookbooks_api/small_universe.json +2987 -0
  101. data/spec/unit/fixtures/cookbooks_api/universe.json +1 -0
  102. data/spec/unit/fixtures/cookbooks_api/update_fixtures.rb +36 -0
  103. data/spec/unit/fixtures/dev_cookbooks/README.md +16 -0
  104. data/spec/unit/fixtures/dev_cookbooks/bar-cookbook.gitbundle +0 -0
  105. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/bin/.keep +0 -0
  106. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/embedded/.keep +0 -0
  107. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/embedded/bin/.keep +0 -0
  108. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/bin/.keep +0 -0
  109. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/apps/berkshelf/.keep +0 -0
  110. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/apps/test-kitchen/.keep +0 -0
  111. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/bin/.keep +0 -0
  112. data/spec/unit/fixtures/eg_omnibus_dir/valid/bin/.keep +0 -0
  113. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/berkshelf/integration_test +2 -0
  114. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/berkshelf/verify_me +5 -0
  115. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/chef-dk/.keep +0 -0
  116. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/chef/verify_me +3 -0
  117. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/test-kitchen/verify_me +2 -0
  118. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/bin/.keep +0 -0
  119. data/spec/unit/fixtures/example_app/Policyfile.rb +0 -0
  120. data/spec/unit/fixtures/example_cookbook/.gitignore +17 -0
  121. data/spec/unit/fixtures/example_cookbook/.kitchen.yml +16 -0
  122. data/spec/unit/fixtures/example_cookbook/Berksfile +3 -0
  123. data/spec/unit/fixtures/example_cookbook/README.md +4 -0
  124. data/spec/unit/fixtures/example_cookbook/chefignore +96 -0
  125. data/spec/unit/fixtures/example_cookbook/metadata.rb +8 -0
  126. data/spec/unit/fixtures/example_cookbook/recipes/default.rb +8 -0
  127. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/.kitchen.yml +16 -0
  128. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/Berksfile +3 -0
  129. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/README.md +4 -0
  130. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/chefignore +96 -0
  131. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/metadata.rb +8 -0
  132. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/recipes/default.rb +8 -0
  133. data/spec/unit/fixtures/local_path_cookbooks/metadata-missing/README.md +2 -0
  134. data/spec/unit/policyfile/chef_server_cookbook_source_spec.rb +34 -0
  135. data/spec/unit/policyfile/community_cookbook_source_spec.rb +51 -0
  136. data/spec/unit/policyfile/cookbook_spec_spec.rb +200 -0
  137. data/spec/unit/policyfile/null_cookbook_source_spec.rb +35 -0
  138. data/spec/unit/policyfile_builder_spec.rb +489 -0
  139. data/spec/unit/policyfile_demands_spec.rb +484 -0
  140. data/spec/unit/policyfile_evaluation_spec.rb +284 -0
  141. data/spec/unit/shell_out_spec.rb +34 -0
  142. metadata +422 -0
@@ -0,0 +1,51 @@
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/community_cookbook_source'
20
+
21
+ describe ChefDK::Policyfile::CommunityCookbookSource do
22
+
23
+ let(:cookbook_source) { ChefDK::Policyfile::CommunityCookbookSource.new }
24
+
25
+ let(:default_community_uri) { "https://supermarket.getchef.com" }
26
+
27
+ let(:http_connection) { double("Chef::HTTP::Simple") }
28
+
29
+ let(:universe_response_encoded) { IO.read(File.join(fixtures_path, "cookbooks_api/small_universe.json")) }
30
+
31
+ let(:pruned_universe) { JSON.parse(IO.read(File.join(fixtures_path, "cookbooks_api/pruned_small_universe.json"))) }
32
+
33
+ before do
34
+ expect(Chef::HTTP::Simple).to receive(:new).with(default_community_uri).and_return(http_connection)
35
+ expect(http_connection).to receive(:get).with("/universe").and_return(universe_response_encoded)
36
+ end
37
+
38
+ it "fetches the universe graph" do
39
+ actual_universe = cookbook_source.universe_graph
40
+ expect(actual_universe).to have_key("apt")
41
+ expect(actual_universe["apt"]).to eq(pruned_universe["apt"])
42
+ expect(cookbook_source.universe_graph).to eq(pruned_universe)
43
+ end
44
+
45
+ it "generates location options for a cookbook from the given graph" do
46
+ expected_opts = { artifactserver: "https://supermarket.getchef.com/api/v1/cookbooks/apache2/versions/1.10.4/download", version: "1.10.4" }
47
+ expect(cookbook_source.source_options_for("apache2", "1.10.4")).to eq(expected_opts)
48
+ end
49
+
50
+ end
51
+
@@ -0,0 +1,200 @@
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/cookbook_spec'
20
+
21
+ describe ChefDK::Policyfile::CookbookSpec do
22
+
23
+ let(:policyfile_filename) { File.join(fixtures_path, "example_app/Policyfile.rb") }
24
+
25
+ let(:version_constraint) { ">= 0.0.0" }
26
+
27
+ let(:cookbook_name) { "my_cookbook" }
28
+
29
+ let(:source_options) { {} }
30
+
31
+ let(:cached_cookbook) { double("ChefDK::CookbookMetadata") }
32
+
33
+ let(:installer) { double("CookbookOmnifetch location", cached_cookbook: cached_cookbook) }
34
+
35
+ let(:cookbook_spec) { described_class.new(cookbook_name, version_constraint, source_options, policyfile_filename) }
36
+
37
+ it "has a name" do
38
+ expect(cookbook_spec.name).to eq(cookbook_name)
39
+ end
40
+
41
+ it "has a version constraint" do
42
+ expect(cookbook_spec.version_constraint).to eq(Semverse::Constraint.new(version_constraint))
43
+ end
44
+
45
+ it "has source options it was created with" do
46
+ expect(cookbook_spec.source_options).to eq(source_options)
47
+ end
48
+
49
+ it "is equal to another cookbook spec with the same name, constraint, and options" do
50
+ equal_spec = described_class.new(cookbook_name, version_constraint, source_options, policyfile_filename)
51
+ expect(cookbook_spec).to eq(equal_spec)
52
+ end
53
+
54
+ it "is not equal to another cookbook spec if the name, constraint or option differ" do
55
+ different_name = described_class.new("wut", version_constraint, source_options, policyfile_filename)
56
+ expect(cookbook_spec).to_not eq(different_name)
57
+
58
+ different_constraint = described_class.new(cookbook_name, ">= 1.0.0", source_options, policyfile_filename)
59
+ expect(cookbook_spec).to_not eq(different_constraint)
60
+
61
+ different_opts = described_class.new(cookbook_name, version_constraint, {git: "git://example.com/wat.git"}, policyfile_filename)
62
+ expect(cookbook_spec).to_not eq(different_opts)
63
+ end
64
+
65
+ it "gives the base directory from which relative paths will be expanded" do
66
+ expect(cookbook_spec.relative_paths_root).to eq(File.join(fixtures_path, "example_app"))
67
+ end
68
+
69
+ describe "fetching and querying a cookbook" do
70
+
71
+ before do
72
+ expect(CookbookOmnifetch).to receive(:init).with(cookbook_spec, source_options).and_return(installer)
73
+ end
74
+
75
+ it "initializes a CookbookOmnifetch location class to handle installation" do
76
+ expect(cookbook_spec.installer).to eq(installer)
77
+ end
78
+
79
+ it "delegates installation to the installer" do
80
+ expect(installer).to receive(:installed?).and_return(false)
81
+ expect(installer).to receive(:install)
82
+ cookbook_spec.ensure_cached
83
+ end
84
+
85
+ it "does not install the cookbook if it's already cached" do
86
+ expect(installer).to receive(:installed?).and_return(true)
87
+ expect(installer).to_not receive(:install)
88
+ cookbook_spec.ensure_cached
89
+ end
90
+
91
+ it "delegates cache_key to the installer" do
92
+ expect(installer).to receive(:cache_key).and_return("my_cookbook-1.2.3-supermarket.getchef.com")
93
+ expect(cookbook_spec.cache_key).to eq("my_cookbook-1.2.3-supermarket.getchef.com")
94
+ end
95
+
96
+ it "delegates relative_path to the installer" do
97
+ expect(installer).to receive(:relative_path).and_return(Pathname.new("../my_stuff/my_cookbook"))
98
+ expect(cookbook_spec.relative_path).to eq("../my_stuff/my_cookbook")
99
+ end
100
+
101
+ it "loads the cookbook metadata via the installer" do
102
+ expect(cookbook_spec.cached_cookbook).to eq(cached_cookbook)
103
+ end
104
+
105
+ it "gives the cookbook's version via the metadata" do
106
+ expect(cached_cookbook).to receive(:version).and_return("1.2.3")
107
+ expect(cookbook_spec.version).to eq("1.2.3")
108
+ end
109
+
110
+ it "gives the cookbook's dependencies via the metadata" do
111
+ expect(cached_cookbook).to receive(:dependencies).and_return("apt" => "~> 1.2.3")
112
+ expect(cookbook_spec.dependencies).to eq("apt" => "~> 1.2.3")
113
+ end
114
+
115
+ end
116
+
117
+ describe "when created with no source" do
118
+
119
+ it "has a nil installer" do
120
+ expect(cookbook_spec.installer).to be_nil
121
+ end
122
+
123
+ it "is not at a fixed version" do
124
+ expect(cookbook_spec.version_fixed?).to be false
125
+ end
126
+
127
+ end
128
+
129
+ describe "when created with a git source" do
130
+
131
+ let(:source_options) { { git: "git@github.com:example/my_cookbook.git" } }
132
+
133
+ it "has a git installer" do
134
+ expect(cookbook_spec.installer).to be_a_kind_of(CookbookOmnifetch::GitLocation)
135
+ end
136
+
137
+ it "has a fixed version" do
138
+ expect(cookbook_spec.version_fixed?).to be true
139
+ end
140
+
141
+ it "mirrors a canonical upstream" do
142
+ expect(cookbook_spec.mirrors_canonical_upstream?).to be true
143
+ end
144
+
145
+ end
146
+
147
+ describe "when created with a github source" do
148
+
149
+ let(:source_options) { { github: "my_org/my_cookbook" } }
150
+
151
+ it "has a github installer" do
152
+ expect(cookbook_spec.installer).to be_a_kind_of(CookbookOmnifetch::GithubLocation)
153
+ end
154
+
155
+ it "has a fixed version" do
156
+ expect(cookbook_spec.version_fixed?).to be true
157
+ end
158
+
159
+ it "mirrors a canonical upstream" do
160
+ expect(cookbook_spec.mirrors_canonical_upstream?).to be true
161
+ end
162
+
163
+ end
164
+
165
+ describe "when created with a path source" do
166
+
167
+ let(:source_options) { { path: "../example_cookbook" } }
168
+
169
+ it "has a path installer" do
170
+ expect(cookbook_spec.installer).to be_a_kind_of(CookbookOmnifetch::PathLocation)
171
+ end
172
+
173
+ it "has a fixed version" do
174
+ expect(cookbook_spec.version_fixed?).to be true
175
+ end
176
+
177
+ it "isnt a mirror of a canonical upstream" do
178
+ expect(cookbook_spec.mirrors_canonical_upstream?).to be false
179
+ end
180
+
181
+ end
182
+
183
+ describe "when created with an artifactserver source" do
184
+
185
+ let(:source_options) { { artifactserver: "https://supermarket.getchef.com:/api/v1/cookbooks/my_cookbook/versions/2.0.0/download" } }
186
+
187
+ it "has a artifactserver installer" do
188
+ expect(cookbook_spec.installer).to be_a_kind_of(CookbookOmnifetch::ArtifactserverLocation)
189
+ end
190
+
191
+ it "does not have a fixed version" do
192
+ expect(cookbook_spec.version_fixed?).to be false
193
+ end
194
+
195
+ it "is a mirror of a canonical upstream" do
196
+ expect(cookbook_spec.mirrors_canonical_upstream?).to be true
197
+ end
198
+
199
+ end
200
+ end
@@ -0,0 +1,35 @@
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
+
20
+ require 'chef-dk/policyfile/null_cookbook_source'
21
+
22
+ describe ChefDK::Policyfile::NullCookbookSource do
23
+
24
+ let(:cookbook_source) { ChefDK::Policyfile::NullCookbookSource.new }
25
+
26
+ it "emits an empty graph" do
27
+ expect(cookbook_source.universe_graph).to eq({})
28
+ end
29
+
30
+ it "emits a not supported error when attempting to get source options for a cookbook" do
31
+ expect { cookbook_source.source_options_for("foo", "1.2.3") }.to raise_error(ChefDK::UnsupportedFeature)
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,489 @@
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/setup_git_cookbooks'
20
+ require 'chef-dk/policyfile_lock.rb'
21
+
22
+ describe ChefDK::PolicyfileLock do
23
+
24
+ def id_to_dotted(sha1_id)
25
+ major = sha1_id[0...14]
26
+ minor = sha1_id[14...28]
27
+ patch = sha1_id[28..40]
28
+ decimal_integers =[major, minor, patch].map {|hex| hex.to_i(16) }
29
+ decimal_integers.join(".")
30
+ end
31
+
32
+ # For debugging giant nested hashes...
33
+ def expect_hash_equal(actual, expected)
34
+ expected.each do |key, expected_value|
35
+ expect(actual).to have_key(key)
36
+ if expected_value.kind_of?(Hash)
37
+ expect_hash_equal(actual[key], expected_value)
38
+ else
39
+ expect(actual[key]).to eq(expected_value)
40
+ end
41
+ end
42
+ expect(actual).to eq(expected)
43
+ end
44
+
45
+ let(:cache_path) do
46
+ File.expand_path("spec/unit/fixtures/cookbook_cache", project_root)
47
+ end
48
+
49
+ let(:relative_paths_root) do
50
+ File.expand_path("spec/unit/fixtures/", project_root)
51
+ end
52
+
53
+ let(:policyfile_lock_options) do
54
+ { cache_path: cache_path, relative_paths_root: relative_paths_root }
55
+ end
56
+
57
+ describe "when first created" do
58
+
59
+ let(:policyfile_lock) do
60
+ described_class.new(policyfile_lock_options)
61
+ end
62
+
63
+ it "uses CWD for relative_paths_root if none is given" do
64
+ policyfile_lock = described_class.new
65
+ expect(policyfile_lock.relative_paths_root).to eq(Dir.pwd)
66
+ end
67
+
68
+ it "uses the provided option for relative_paths_root" do
69
+ expect(policyfile_lock.relative_paths_root).to eq(relative_paths_root)
70
+ end
71
+
72
+ it "uses the provided cache_path" do
73
+ expect(policyfile_lock.cache_path).to eq(cache_path)
74
+ end
75
+
76
+ end
77
+
78
+ context "when a cookbook is not in the cache" do
79
+
80
+ let(:policyfile_lock) do
81
+ ChefDK::PolicyfileLock.build(policyfile_lock_options) do |p|
82
+
83
+ p.name = "invalid_cache_key_policyfile"
84
+
85
+ p.run_list = [ "recipe[foo]" ]
86
+
87
+ p.cached_cookbook("nosuchthing") do |cb|
88
+ cb.cache_key = "nosuchthing-1.0.0"
89
+ end
90
+ end
91
+ end
92
+
93
+ it "raises a descriptive error" do
94
+ expect { policyfile_lock.to_lock }.to raise_error(ChefDK::CachedCookbookNotFound)
95
+ end
96
+
97
+ end
98
+
99
+ context "with a minimal policyfile" do
100
+
101
+ let(:policyfile_lock) do
102
+ ChefDK::PolicyfileLock.build(policyfile_lock_options) do |p|
103
+
104
+ p.name = "minimal_policyfile"
105
+
106
+ p.run_list = [ "recipe[foo]" ]
107
+ p.cached_cookbook("foo") do |cb|
108
+ cb.cache_key = "foo-1.0.0"
109
+ end
110
+
111
+ end
112
+ end
113
+
114
+ let(:compiled_policyfile) do
115
+ {
116
+
117
+ "name" => "minimal_policyfile",
118
+
119
+ "run_list" => ["recipe[foo]"],
120
+
121
+ "cookbook_locks" => {
122
+
123
+ "foo" => {
124
+ "version" => "1.0.0",
125
+ "identifier" => "e4611e9b5ec0636a18979e7dd22537222a2eab47",
126
+ "dotted_decimal_identifier" => id_to_dotted("e4611e9b5ec0636a18979e7dd22537222a2eab47"),
127
+ "cache_key" => "foo-1.0.0",
128
+ "origin" => nil
129
+ },
130
+ }
131
+ }
132
+ end
133
+
134
+ it "has a cache path" do
135
+ expect(policyfile_lock.cache_path).to eq(cache_path)
136
+ end
137
+
138
+ it "computes a minimal policyfile" do
139
+ expect(policyfile_lock.to_lock).to eq(compiled_policyfile)
140
+ end
141
+
142
+ end
143
+
144
+ context "with a policyfile containing a local cookbook" do
145
+
146
+ include_context "setup git cookbooks"
147
+ include_context "setup git cookbook remote"
148
+
149
+ let(:relative_paths_root) do
150
+ tempdir
151
+ end
152
+
153
+ let(:policyfile_lock) do
154
+ ChefDK::PolicyfileLock.build(policyfile_lock_options) do |p|
155
+
156
+ p.name = "dev_cookbook"
157
+
158
+ p.run_list = [ "recipe[bar]" ]
159
+ p.local_cookbook("bar") do |cb|
160
+ cb.source = "bar"
161
+ end
162
+
163
+ end
164
+ end
165
+
166
+ let(:compiled_policyfile) do
167
+ {
168
+
169
+ "name" => "dev_cookbook",
170
+
171
+ "run_list" => ["recipe[bar]"],
172
+
173
+ "cookbook_locks" => {
174
+
175
+ "bar" => {
176
+ "version" => "0.1.0",
177
+ "identifier" => "f7694dbebe4109dfc857af7e2e4475c322c65259",
178
+ "dotted_decimal_identifier" => id_to_dotted("f7694dbebe4109dfc857af7e2e4475c322c65259"),
179
+
180
+ "source" => "bar",
181
+ "cache_key" => nil,
182
+ "scm_info" => {
183
+ "scm" => "git",
184
+ "remote" => remote_url,
185
+ "revision" => current_rev,
186
+ "working_tree_clean" => true,
187
+ "published" => true,
188
+ "synchronized_remote_branches"=>["origin/master"]
189
+ },
190
+ },
191
+ }
192
+ }
193
+ end
194
+
195
+ it "computes a lockfile including git data" do
196
+ actual_lock = policyfile_lock.to_lock
197
+ expect(actual_lock).to eq(compiled_policyfile)
198
+ end
199
+ end
200
+
201
+ context "with a policyfile using custom identifiers" do
202
+
203
+ include_context "setup git cookbooks"
204
+
205
+ let(:relative_paths_root) do
206
+ tempdir
207
+ end
208
+
209
+ let(:policyfile_lock) do
210
+ ChefDK::PolicyfileLock.build(policyfile_lock_options) do |p|
211
+
212
+ p.name = "custom_identifier"
213
+
214
+ p.run_list = [ "recipe[foo]" ]
215
+
216
+ p.cached_cookbook("foo") do |cb|
217
+ cb.cache_key = "foo-1.0.0"
218
+
219
+ # Explicitly set the identifier and dotted decimal identifiers to the
220
+ # version number (but it could be anything).
221
+ cb.identifier = "1.0.0"
222
+ cb.dotted_decimal_identifier ="1.0.0"
223
+ end
224
+
225
+ p.local_cookbook("bar") do |cb|
226
+ cb.source = "bar"
227
+ cb.identifier = "0.1.0"
228
+ cb.dotted_decimal_identifier = "0.1.0"
229
+ end
230
+ end
231
+
232
+ end
233
+
234
+ let(:compiled_policyfile) do
235
+ {
236
+
237
+ "name" => "custom_identifier",
238
+
239
+ "run_list" => ["recipe[foo]"],
240
+
241
+ "cookbook_locks" => {
242
+
243
+ "foo" => {
244
+ "version" => "1.0.0",
245
+ "identifier" => "1.0.0",
246
+ "dotted_decimal_identifier" => "1.0.0",
247
+ "cache_key" => "foo-1.0.0",
248
+ "origin" => nil
249
+ },
250
+
251
+ "bar" => {
252
+ "version" => "0.1.0",
253
+ "identifier" => "0.1.0",
254
+ "dotted_decimal_identifier" => "0.1.0",
255
+
256
+ "source" => "bar",
257
+ "cache_key" => nil,
258
+ "scm_info" => {
259
+ "scm" => "git",
260
+ "remote" => nil,
261
+ "revision" => current_rev,
262
+ "working_tree_clean" => true,
263
+ "published" => false,
264
+ "synchronized_remote_branches"=>[]
265
+ },
266
+ },
267
+ }
268
+ }
269
+ end
270
+
271
+ it "generates a lockfile with custom identifiers" do
272
+ expect(policyfile_lock.to_lock).to eq(compiled_policyfile)
273
+ end
274
+
275
+ end
276
+
277
+ context "with a policyfile lock with a mix of cached and local cookbooks" do
278
+
279
+ include_context "setup git cookbooks"
280
+
281
+ let(:relative_paths_root) do
282
+ tempdir
283
+ end
284
+
285
+ let(:policyfile_lock) do
286
+
287
+ ChefDK::PolicyfileLock.build(policyfile_lock_options) do |p|
288
+
289
+ # Required
290
+ p.name = "basic_example"
291
+
292
+ # Required. Should be fully expanded without roles
293
+ p.run_list = ["recipe[foo]", "recipe[bar]", "recipe[baz::non_default]"]
294
+
295
+ # A cached_cookbook is stored in the cache directory in a subdirectory
296
+ # given by 'cache_key'. It is assumed to be static (not modified by the
297
+ # user).
298
+ p.cached_cookbook("foo") do |cb|
299
+ cb.cache_key = "foo-1.0.0"
300
+
301
+ # Optional attribute that humans can use to understand where a cookbook
302
+ # came from.
303
+ cb.origin = "https://community.getchef.com/api/cookbooks/foo/1.0.0"
304
+ end
305
+
306
+ p.local_cookbook("bar") do |cb|
307
+ cb.source = "bar"
308
+ end
309
+
310
+ p.cached_cookbook("baz") do |cb|
311
+ cb.cache_key = "baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb"
312
+ cb.origin = "git://github.com/opscode-cookbooks/bar.git"
313
+ end
314
+
315
+ p.cached_cookbook("dep_of_bar") do |cb|
316
+ cb.cache_key = "dep_of_bar-1.2.3"
317
+ cb.origin = "https://chef-server.example.com/cookbooks/dep_of_bar/1.2.3"
318
+ end
319
+ end
320
+
321
+ end
322
+
323
+
324
+ let(:compiled_policyfile) do
325
+ {
326
+
327
+ "name" => "basic_example",
328
+
329
+ "run_list" => ["recipe[foo]", "recipe[bar]", "recipe[baz::non_default]"],
330
+
331
+ "cookbook_locks" => {
332
+
333
+ "foo" => {
334
+ "version" => "1.0.0",
335
+ "identifier" => "e4611e9b5ec0636a18979e7dd22537222a2eab47",
336
+ "dotted_decimal_identifier" => id_to_dotted("e4611e9b5ec0636a18979e7dd22537222a2eab47"),
337
+ "origin" => "https://community.getchef.com/api/cookbooks/foo/1.0.0",
338
+ "cache_key" => "foo-1.0.0"
339
+ },
340
+
341
+ "bar" => {
342
+ "version" => "0.1.0",
343
+ "identifier" => "f7694dbebe4109dfc857af7e2e4475c322c65259",
344
+ "dotted_decimal_identifier" => id_to_dotted("f7694dbebe4109dfc857af7e2e4475c322c65259"),
345
+ "source" => "bar",
346
+ "cache_key" => nil,
347
+
348
+ "scm_info" => {
349
+ "scm" => "git",
350
+ "remote" => nil,
351
+ "revision" => current_rev,
352
+ "working_tree_clean" => true,
353
+ "published" => false,
354
+ "synchronized_remote_branches"=>[]
355
+ },
356
+ },
357
+
358
+ "baz" => {
359
+ "version" => "1.2.3",
360
+ "identifier"=>"08c6ac1d202f4d59ad67953559084886f6ba710a",
361
+ "dotted_decimal_identifier" => id_to_dotted("08c6ac1d202f4d59ad67953559084886f6ba710a"),
362
+ "cache_key" => "baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb",
363
+ "origin" => "git://github.com/opscode-cookbooks/bar.git"
364
+ },
365
+
366
+ "dep_of_bar" => {
367
+ "version" => "1.2.3",
368
+ "identifier" => "e6c08ea35bce8009386710d8c9bcd6caa036e8bc",
369
+ "dotted_decimal_identifier" => id_to_dotted("e6c08ea35bce8009386710d8c9bcd6caa036e8bc"),
370
+ "origin" => "https://chef-server.example.com/cookbooks/dep_of_bar/1.2.3",
371
+ "cache_key" => "dep_of_bar-1.2.3",
372
+
373
+ },
374
+
375
+ },
376
+
377
+ }
378
+ end
379
+
380
+ it "generates a lockfile with the relevant profile data for each cookbook" do
381
+ generated = policyfile_lock.to_lock
382
+ expect(generated['name']).to eq(compiled_policyfile['name'])
383
+ expect(generated['run_list']).to eq(compiled_policyfile['run_list'])
384
+
385
+ generated_locks = generated['cookbook_locks']
386
+ expected_locks = compiled_policyfile['cookbook_locks']
387
+
388
+ # test individually so failures are easier to read
389
+ expect(generated_locks['foo']).to eq(expected_locks['foo'])
390
+ expect(generated_locks['bar']).to eq(expected_locks['bar'])
391
+ expect(generated_locks['baz']).to eq(expected_locks['baz'])
392
+ expect(generated_locks['dep_of_bar']).to eq(expected_locks['dep_of_bar'])
393
+
394
+ expect(policyfile_lock.to_lock).to eq(compiled_policyfile)
395
+ end
396
+
397
+ end
398
+
399
+ describe "building a policyfile lock from a policyfile compiler" do
400
+
401
+ include_context "setup git cookbooks"
402
+
403
+ let(:relative_paths_root) do
404
+ tempdir
405
+ end
406
+
407
+ let(:cached_cookbook_spec) do
408
+ double( "ChefDK::Policyfile::CookbookSpec",
409
+ mirrors_canonical_upstream?: true,
410
+ cache_key: "foo-1.0.0",
411
+ uri: "https://supermarket.getchef.com/api/v1/cookbooks/foo/versions/1.0.0/download")
412
+ end
413
+
414
+ let(:local_cookbook_spec) do
415
+ double( "ChefDK::Policyfile::CookbookSpec",
416
+ mirrors_canonical_upstream?: false,
417
+ relative_paths_root: relative_paths_root,
418
+ relative_path: "bar")
419
+ end
420
+
421
+
422
+ let(:policyfile_compiler) do
423
+ double( "ChefDK::PolicyfileCompiler",
424
+ expanded_run_list: %w[foo bar],
425
+ all_cookbook_specs: {"foo" => cached_cookbook_spec, "bar" => local_cookbook_spec})
426
+ end
427
+
428
+ let(:policyfile_lock) do
429
+ ChefDK::PolicyfileLock.build_from_compiler(policyfile_compiler, cache_path: cache_path)
430
+ end
431
+
432
+ let(:compiled_policyfile) do
433
+ {
434
+
435
+ "name" => nil,
436
+
437
+ "run_list" => ["foo", "bar"],
438
+
439
+ "cookbook_locks" => {
440
+
441
+ "foo" => {
442
+ "version" => "1.0.0",
443
+ "identifier" => "e4611e9b5ec0636a18979e7dd22537222a2eab47",
444
+ "dotted_decimal_identifier" => id_to_dotted("e4611e9b5ec0636a18979e7dd22537222a2eab47"),
445
+ "cache_key" => "foo-1.0.0",
446
+ "origin" => cached_cookbook_spec.uri
447
+ },
448
+
449
+ "bar" => {
450
+ "version" => "0.1.0",
451
+ "identifier" => "f7694dbebe4109dfc857af7e2e4475c322c65259",
452
+ "dotted_decimal_identifier" => id_to_dotted("f7694dbebe4109dfc857af7e2e4475c322c65259"),
453
+ "source" => "bar",
454
+ "cache_key" => nil,
455
+
456
+ "scm_info" => {
457
+ "scm" => "git",
458
+ "remote" => nil,
459
+ "revision" => current_rev,
460
+ "working_tree_clean" => true,
461
+ "published" => false,
462
+ "synchronized_remote_branches"=>[]
463
+ }
464
+ }
465
+ }
466
+ }
467
+ end
468
+
469
+
470
+ it "adds a cached cookbook lock generator for the compiler's cached cookbook" do
471
+ expect(policyfile_lock.cookbook_locks).to have_key("foo")
472
+ cb_lock = policyfile_lock.cookbook_locks["foo"]
473
+ expect(cb_lock.origin).to eq(cached_cookbook_spec.uri)
474
+ expect(cb_lock.cache_key).to eq(cached_cookbook_spec.cache_key)
475
+ end
476
+
477
+ it "adds a local cookbook lock generator for the compiler's local cookbook" do
478
+ expect(policyfile_lock.cookbook_locks).to have_key("bar")
479
+ cb_lock = policyfile_lock.cookbook_locks["bar"]
480
+ expect(cb_lock.source).to eq(local_cookbook_spec.relative_path)
481
+ end
482
+
483
+ it "generates a lockfile data structure" do
484
+ expect(policyfile_lock.to_lock).to eq(compiled_policyfile)
485
+ end
486
+
487
+ end
488
+
489
+ end