chef-dk 0.2.0

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