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,46 @@
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 'chef-dk/exceptions'
19
+
20
+ module ChefDK
21
+ module Policyfile
22
+ class ChefServerCookbookSource
23
+
24
+ attr_reader :uri
25
+
26
+ def initialize(uri)
27
+ @uri = uri
28
+ end
29
+
30
+ def ==(other)
31
+ other.kind_of?(self.class) && other.uri == uri
32
+ end
33
+
34
+ def universe_graph
35
+ raise UnsupportedFeature, 'ChefDK does not support chef-server cookbook default sources at this time'
36
+ end
37
+
38
+ def source_options_for(cookbook_name, cookbook_version)
39
+ raise UnsupportedFeature, 'ChefDK does not support chef-server cookbook default sources at this time'
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
46
+
@@ -0,0 +1,84 @@
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 'json'
19
+ require 'chef-dk/cookbook_omnifetch'
20
+ require 'chef-dk/exceptions'
21
+ # TODO: chef bug. Chef::HTTP::Simple needs to require this itself.
22
+ # Fixed in 2ed829e661f9a357fc9a8cdf316c84f077dad7f9 waiting for that to be
23
+ # released...
24
+ require 'tempfile'
25
+ require 'chef/platform/query_helpers' # should be handled by http/simple
26
+ require 'chef/http/cookie_manager' # should be handled by http/simple
27
+ require 'chef/http/validate_content_length' # should be handled by http/simple
28
+ require 'chef/http/simple'
29
+
30
+ # TODO: fix hardcoding
31
+ Chef::Config.ssl_verify_mode = :verify_peer
32
+
33
+ module ChefDK
34
+ module Policyfile
35
+
36
+ class CommunityCookbookSource
37
+
38
+ attr_reader :uri
39
+
40
+ def initialize(uri = nil)
41
+ @uri = uri || "https://supermarket.getchef.com"
42
+ @http_connections = {}
43
+ end
44
+
45
+ def ==(other)
46
+ other.kind_of?(self.class) && other.uri == uri
47
+ end
48
+
49
+ def universe_graph
50
+ @universe_graph ||= begin
51
+ full_community_graph.inject({}) do |normalized_graph, (cookbook_name, metadata_by_version)|
52
+ normalized_graph[cookbook_name] = metadata_by_version.inject({}) do |deps_by_version, (version, metadata)|
53
+ deps_by_version[version] = metadata["dependencies"]
54
+ deps_by_version
55
+ end
56
+ normalized_graph
57
+ end
58
+ end
59
+ end
60
+
61
+ def source_options_for(cookbook_name, cookbook_version)
62
+ base_uri = full_community_graph[cookbook_name][cookbook_version]["download_url"]
63
+ { artifactserver: base_uri, version: cookbook_version }
64
+ end
65
+
66
+ private
67
+
68
+ def http_connection_for(base_url)
69
+ @http_connections[base_url] ||= Chef::HTTP::Simple.new(base_url)
70
+ end
71
+
72
+
73
+ def full_community_graph
74
+ @full_community_graph ||=
75
+ begin
76
+ graph_json = http_connection_for(uri).get("/universe")
77
+ JSON.parse(graph_json)
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+
@@ -0,0 +1,20 @@
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 'chef-dk/policyfile/null_cookbook_source'
19
+ require 'chef-dk/policyfile/community_cookbook_source'
20
+ require 'chef-dk/policyfile/chef_server_cookbook_source'
@@ -0,0 +1,96 @@
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 'semverse'
19
+ require 'cookbook-omnifetch'
20
+
21
+ module ChefDK
22
+ module Policyfile
23
+
24
+ class CookbookSpec
25
+
26
+ SOURCE_TYPES = [:git, :github, :path, :artifactserver]
27
+
28
+ attr_reader :version_constraint
29
+ attr_reader :name
30
+ attr_reader :source_options
31
+ attr_reader :source_type
32
+
33
+ def initialize(name, version_constraint, source_options, policyfile_filename)
34
+ @name = name
35
+ @version_constraint = Semverse::Constraint.new(version_constraint)
36
+ @source_options = source_options
37
+ @policyfile_filename = policyfile_filename
38
+ @source_type = SOURCE_TYPES.find { |type| source_options.key?(type) }
39
+ end
40
+
41
+ def ==(other)
42
+ other.kind_of?(self.class) and
43
+ other.name == name and
44
+ other.version_constraint == version_constraint and
45
+ other.source_options == source_options
46
+ end
47
+
48
+ def mirrors_canonical_upstream?
49
+ [:git, :github, :artifactserver].include?(source_type)
50
+ end
51
+
52
+ def ensure_cached
53
+ unless installer.installed?
54
+ installer.install
55
+ end
56
+ end
57
+
58
+ def installer
59
+ @installer ||= CookbookOmnifetch.init(self, source_options)
60
+ end
61
+
62
+ def cache_key
63
+ installer.cache_key
64
+ end
65
+
66
+ def relative_path
67
+ installer.relative_path.to_s
68
+ end
69
+
70
+ def uri
71
+ installer.uri
72
+ end
73
+
74
+ def version_fixed?
75
+ [:git, :github, :path].include?(@source_type)
76
+ end
77
+
78
+ def version
79
+ cached_cookbook.version
80
+ end
81
+
82
+ def dependencies
83
+ cached_cookbook.dependencies
84
+ end
85
+
86
+ def cached_cookbook
87
+ installer.cached_cookbook
88
+ end
89
+
90
+ def relative_paths_root
91
+ File.dirname(@policyfile_filename)
92
+ end
93
+
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,148 @@
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 'chef-dk/policyfile/cookbook_sources'
19
+ require 'chef-dk/policyfile/cookbook_spec'
20
+
21
+ module ChefDK
22
+ module Policyfile
23
+ class DSL
24
+
25
+ attr_reader :errors
26
+ attr_reader :run_list
27
+ attr_reader :default_source
28
+ attr_reader :policyfile_cookbook_specs
29
+
30
+ attr_accessor :policyfile_filename
31
+
32
+ def initialize
33
+ @errors = []
34
+ @run_list = []
35
+ @default_source = NullCookbookSource.new
36
+ @policyfile_cookbook_specs = {}
37
+ @policyfile_filename = nil
38
+ end
39
+
40
+ def run_list(*run_list_items)
41
+ run_list_items = run_list_items.flatten
42
+ @run_list = run_list_items unless run_list_items.empty?
43
+ @run_list
44
+ end
45
+
46
+ def default_source(source_type = nil, source_uri = nil)
47
+ return @default_source if source_type.nil?
48
+ case source_type
49
+ when :community
50
+ set_default_community_source(source_uri)
51
+ when :chef_server
52
+ set_default_chef_server_source(source_uri)
53
+ else
54
+ @errors << "Invalid default_source type '#{source_type.inspect}'"
55
+ end
56
+ end
57
+
58
+ def cookbook(name, *version_and_source_opts)
59
+ source_options =
60
+ if version_and_source_opts.last.is_a?(Hash)
61
+ version_and_source_opts.pop
62
+ else
63
+ {}
64
+ end
65
+
66
+ constraint = version_and_source_opts.first || ">= 0.0.0"
67
+ spec = CookbookSpec.new(name, constraint, source_options, policyfile_filename)
68
+
69
+
70
+ if existing_source = @policyfile_cookbook_specs[name]
71
+ err = "Cookbook '#{name}' assigned to conflicting sources\n\n"
72
+ err << "Previous source: #{existing_source.source_options.inspect}\n"
73
+ err << "Conflicts with: #{source_options.inspect}\n"
74
+ @errors << err
75
+ else
76
+ @policyfile_cookbook_specs[name] = spec
77
+ end
78
+ end
79
+
80
+ def eval_policyfile(policyfile_string, policyfile_filename)
81
+ @policyfile_filename = policyfile_filename
82
+ instance_eval(policyfile_string, policyfile_filename)
83
+ validate!
84
+ self
85
+ rescue SyntaxError => e
86
+ @errors << "Invalid ruby syntax in policyfile '#{policyfile_filename}':\n\n#{e.message}"
87
+ rescue SignalException, SystemExit
88
+ # allow signal from kill, ctrl-C, etc. to bubble up:
89
+ raise
90
+ rescue Exception => e
91
+ error_message = "Evaluation of policyfile '#{policyfile_filename}' raised an exception\n"
92
+ error_message << " Exception: #{e.class.name} \"#{e.to_s}\"\n\n"
93
+ trace = filtered_bt(policyfile_filename, e)
94
+ error_message << " Relevant Code:\n"
95
+ error_message << " #{error_context(policyfile_string, policyfile_filename, e)}\n\n"
96
+ unless trace.empty?
97
+ error_message << " Backtrace:\n"
98
+ error_message << filtered_bt(policyfile_filename, e).inject("") { |formatted_trace, line| formatted_trace << " #{line}" }
99
+ error_message << "\n"
100
+ end
101
+ @errors << error_message
102
+ end
103
+
104
+ private
105
+
106
+ def set_default_community_source(source_uri)
107
+ @default_source = CommunityCookbookSource.new(source_uri)
108
+ end
109
+
110
+ def set_default_chef_server_source(source_uri)
111
+ if source_uri.nil?
112
+ @errors << "You must specify the server's URI when using a default_source :chef_server"
113
+ else
114
+ @default_source = ChefServerCookbookSource.new(source_uri)
115
+ end
116
+ end
117
+
118
+ def validate!
119
+ if @run_list.empty?
120
+ @errors << "Invalid run_list. run_list cannot be empty"
121
+ end
122
+ end
123
+
124
+ def error_context(policyfile_string, policyfile_filename, exception)
125
+ if line_number_to_show = culprit_line_number(policyfile_filename, exception)
126
+ code = policyfile_string.lines.to_a[line_number_to_show - 1].strip
127
+ "#{line_number_to_show}: #{code}"
128
+ else
129
+ "Could not find relevant code from backtrace"
130
+ end
131
+ end
132
+
133
+ def culprit_line_number(policyfile_filename, exception)
134
+ if most_proximate_backtrace_line = filtered_bt(policyfile_filename, exception).first
135
+ most_proximate_backtrace_line[/^(?:.\:)?[^:]+:([\d]+)/,1].to_i
136
+ else
137
+ nil
138
+ end
139
+ end
140
+
141
+ def filtered_bt(policyfile_filename, exception)
142
+ policyfile_filename_matcher = /^#{Regexp.escape(policyfile_filename)}/
143
+ exception.backtrace.select {|line| line =~ policyfile_filename_matcher }
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,37 @@
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 'chef-dk/exceptions'
19
+
20
+ module ChefDK
21
+ module Policyfile
22
+ class NullCookbookSource
23
+
24
+ def initialize(_uri=nil)
25
+ end
26
+
27
+ def universe_graph
28
+ {}
29
+ end
30
+
31
+ def source_options_for(cookbook_name, cookbook_version)
32
+ raise UnsupportedFeature, 'You must set a default_source in your Policyfile to download cookbooks without explicit sources'
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,217 @@
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 'forwardable'
19
+
20
+ require 'solve'
21
+ require 'chef/run_list/run_list_item'
22
+
23
+ require 'chef-dk/policyfile/dsl'
24
+ require 'chef-dk/policyfile_lock'
25
+
26
+ module ChefDK
27
+
28
+ class PolicyfileCompiler
29
+
30
+ extend Forwardable
31
+
32
+ DEFAULT_DEMAND_CONSTRAINT = '>= 0.0.0'.freeze
33
+
34
+ # Cookbooks from these sources lock that cookbook to exactly one version
35
+ SOURCE_TYPES_WITH_FIXED_VERSIONS = [:git, :path].freeze
36
+
37
+ def self.evaluate(policyfile_string, policyfile_filename)
38
+ compiler = new
39
+ compiler.evaluate_policyfile(policyfile_string, policyfile_filename)
40
+ compiler
41
+ end
42
+
43
+ def_delegator :@dsl, :run_list
44
+ def_delegator :@dsl, :errors
45
+ def_delegator :@dsl, :default_source
46
+ def_delegator :@dsl, :policyfile_cookbook_specs
47
+
48
+ attr_reader :dsl
49
+
50
+ def initialize
51
+ @dsl = Policyfile::DSL.new
52
+ @artifact_server_cookbook_specs = {}
53
+ end
54
+
55
+ def error!
56
+ unless errors.empty?
57
+ raise PolicyfileError, errors.join("\n")
58
+ end
59
+ end
60
+
61
+ def cookbook_spec_for(cookbook_name)
62
+ policyfile_cookbook_specs[cookbook_name]
63
+ end
64
+
65
+ def expanded_run_list
66
+ run_list
67
+ end
68
+
69
+ def lock
70
+ @policyfile_lock ||= PolicyfileLock.build_from_compiler(self, cache_path: cache_path)
71
+ end
72
+
73
+ def install
74
+ ensure_cache_dir_exists
75
+
76
+ graph_solution.each do |cookbook_name, version|
77
+ spec = cookbook_spec_for(cookbook_name)
78
+ if spec.nil? or !spec.version_fixed?
79
+ spec = create_spec_for_cookbook(cookbook_name, version)
80
+ spec.ensure_cached
81
+ end
82
+ end
83
+ end
84
+
85
+ def create_spec_for_cookbook(cookbook_name, version)
86
+ source_options = default_source.source_options_for(cookbook_name, version)
87
+ spec = Policyfile::CookbookSpec.new(cookbook_name, "= #{version}", source_options, dsl)
88
+ @artifact_server_cookbook_specs[cookbook_name] = spec
89
+ end
90
+
91
+ def all_cookbook_specs
92
+ # in the installation proces, we create "artifact_server_cookbook_specs"
93
+ # for any cookbook that isn't sourced from a single-version source (e.g.,
94
+ # path and git only support one version at a time), but we might have
95
+ # specs for them to track additional version constraint demands. Merging
96
+ # in this order ensures the artifact_server_cookbook_specs "win".
97
+ policyfile_cookbook_specs.merge(@artifact_server_cookbook_specs)
98
+ end
99
+
100
+ ##
101
+ # Compilation Methods
102
+ ##
103
+
104
+ def graph_solution
105
+ return @solution if @solution
106
+ cache_fixed_version_cookbooks
107
+ @solution = Solve.it!(graph, graph_demands)
108
+ end
109
+
110
+ def graph
111
+ @graph ||= Solve::Graph.new.tap do |g|
112
+ artifacts_graph.each do |name, dependencies_by_version|
113
+ dependencies_by_version.each do |version, dependencies|
114
+ artifact = g.artifact(name, version)
115
+ dependencies.each do |dep_name, constraint|
116
+ artifact.dependency(dep_name, constraint)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ def graph_demands
124
+ cookbooks_for_demands.map do |cookbook_name|
125
+ spec = cookbook_spec_for(cookbook_name)
126
+ if spec.nil?
127
+ [ cookbook_name, DEFAULT_DEMAND_CONSTRAINT ]
128
+ elsif spec.version_fixed?
129
+ [ cookbook_name, "= #{spec.version}" ]
130
+ else
131
+ [ cookbook_name, spec.version_constraint.to_s ]
132
+ end
133
+ end
134
+ end
135
+
136
+ def artifacts_graph
137
+ remote_artifacts_graph.merge(local_artifacts_graph)
138
+ end
139
+
140
+ # Gives a dependency graph for cookbooks that are source from an alternate
141
+ # location. These cookbooks could have a different set of dependencies
142
+ # compared to an unmodified copy upstream. For example, the community site
143
+ # may have a cookbook "apache2" at version "1.10.4", which the user has
144
+ # forked on github and modified the dependencies without changing the
145
+ # version number. To accomodate this, the local_artifacts_graph should be
146
+ # merged over the upstream's artifacts graph.
147
+ def local_artifacts_graph
148
+ policyfile_cookbook_specs.inject({}) do |local_artifacts, (cookbook_name, cookbook_spec)|
149
+ if cookbook_spec.version_fixed?
150
+ local_artifacts[cookbook_name] = { cookbook_spec.version => cookbook_spec.dependencies }
151
+ end
152
+ local_artifacts
153
+ end
154
+ end
155
+
156
+ def remote_artifacts_graph
157
+ default_source.universe_graph
158
+ end
159
+
160
+ def version_constraint_for(cookbook_name)
161
+ if (cookbook_spec = cookbook_spec_for(cookbook_name)) and cookbook_spec.version_fixed?
162
+ version = cookbook_spec.version
163
+ "= #{version}"
164
+ else
165
+ DEFAULT_DEMAND_CONSTRAINT
166
+ end
167
+ end
168
+
169
+ def cookbook_version_fixed?(cookbook_name)
170
+ if cookbook_spec = cookbook_spec_for(cookbook_name)
171
+ cookbook_spec.version_fixed?
172
+ else
173
+ false
174
+ end
175
+ end
176
+
177
+ def cookbooks_in_run_list
178
+ run_list.map {|item_spec| Chef::RunList::RunListItem.new(item_spec).name }
179
+ end
180
+
181
+ def build
182
+ yield @dsl
183
+ self
184
+ end
185
+
186
+ def evaluate_policyfile(policyfile_string, policyfile_filename)
187
+ @dsl.eval_policyfile(policyfile_string, policyfile_filename)
188
+ self
189
+ end
190
+
191
+ private
192
+
193
+ def cookbooks_for_demands
194
+ (cookbooks_in_run_list + policyfile_cookbook_specs.keys).uniq
195
+ end
196
+
197
+ def cache_fixed_version_cookbooks
198
+ ensure_cache_dir_exists
199
+
200
+ policyfile_cookbook_specs.each do |_cookbook_name, cookbook_spec|
201
+ cookbook_spec.ensure_cached if cookbook_spec.version_fixed?
202
+ end
203
+ end
204
+
205
+ def ensure_cache_dir_exists
206
+ unless File.exist?(cache_path)
207
+ FileUtils.mkdir_p(cache_path)
208
+ end
209
+ end
210
+
211
+ def cache_path
212
+ CookbookOmnifetch.storage_path
213
+ end
214
+
215
+
216
+ end
217
+ end