chef-dk 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/chef-dk/authenticated_http.rb +40 -0
  4. data/lib/chef-dk/chef_runner.rb +5 -0
  5. data/lib/chef-dk/command/exec.rb +4 -1
  6. data/lib/chef-dk/command/generate.rb +11 -0
  7. data/lib/chef-dk/command/generator_commands.rb +20 -365
  8. data/lib/chef-dk/command/generator_commands/app.rb +99 -0
  9. data/lib/chef-dk/command/generator_commands/attribute.rb +37 -0
  10. data/lib/chef-dk/command/generator_commands/base.rb +76 -0
  11. data/lib/chef-dk/command/generator_commands/cookbook.rb +100 -0
  12. data/lib/chef-dk/command/generator_commands/cookbook_code_file.rb +99 -0
  13. data/lib/chef-dk/command/generator_commands/cookbook_file.rb +45 -0
  14. data/lib/chef-dk/command/generator_commands/lwrp.rb +36 -0
  15. data/lib/chef-dk/command/generator_commands/recipe.rb +36 -0
  16. data/lib/chef-dk/command/generator_commands/repo.rb +96 -0
  17. data/lib/chef-dk/command/generator_commands/template.rb +45 -0
  18. data/lib/chef-dk/command/verify.rb +28 -0
  19. data/lib/chef-dk/component_test.rb +16 -3
  20. data/lib/chef-dk/cookbook_omnifetch.rb +2 -0
  21. data/lib/chef-dk/cookbook_profiler/identifiers.rb +3 -15
  22. data/lib/chef-dk/exceptions.rb +15 -0
  23. data/lib/chef-dk/generator.rb +102 -25
  24. data/lib/chef-dk/policyfile/community_cookbook_source.rb +0 -7
  25. data/lib/chef-dk/policyfile/{cookbook_spec.rb → cookbook_location_specification.rb} +35 -6
  26. data/lib/chef-dk/policyfile/cookbook_locks.rb +305 -0
  27. data/lib/chef-dk/policyfile/dsl.rb +26 -12
  28. data/lib/chef-dk/policyfile/read_cookbook_for_compat_mode_upload.rb +70 -0
  29. data/lib/chef-dk/policyfile/solution_dependencies.rb +204 -0
  30. data/lib/chef-dk/policyfile/storage_config.rb +77 -0
  31. data/lib/chef-dk/policyfile/uploader.rb +110 -0
  32. data/lib/chef-dk/policyfile_compiler.rb +59 -29
  33. data/lib/chef-dk/policyfile_lock.rb +104 -160
  34. data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +1 -1
  35. data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +0 -1
  36. data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +66 -0
  37. data/lib/chef-dk/skeletons/code_generator/files/default/repo/Rakefile +65 -0
  38. data/lib/chef-dk/skeletons/code_generator/files/default/repo/certificates/README.md +19 -0
  39. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/README-policy.md +9 -0
  40. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/README.md +54 -0
  41. data/lib/chef-dk/skeletons/code_generator/files/default/repo/data_bags/README.md +63 -0
  42. data/lib/chef-dk/skeletons/code_generator/files/default/repo/environments/README.md +5 -0
  43. data/lib/chef-dk/skeletons/code_generator/files/default/repo/roles/README.md +16 -0
  44. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +7 -1
  45. data/lib/chef-dk/skeletons/code_generator/recipes/repo.rb +62 -0
  46. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.all_rights.erb +3 -0
  47. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.apache2.erb +201 -0
  48. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.gplv2.erb +339 -0
  49. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.gplv3.erb +674 -0
  50. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.mit.erb +21 -0
  51. data/lib/chef-dk/skeletons/code_generator/templates/default/default_recipe.rb.erb +1 -4
  52. data/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb +3 -3
  53. data/lib/chef-dk/skeletons/code_generator/templates/default/repo/config/rake.rb.erb +38 -0
  54. data/lib/chef-dk/skeletons/code_generator/templates/default/repo/gitignore.erb +11 -0
  55. data/lib/chef-dk/version.rb +1 -1
  56. data/spec/shared/a_file_generator.rb +121 -0
  57. data/spec/shared/a_generated_file.rb +12 -0
  58. data/spec/shared/fixture_cookbook_checksums.rb +47 -0
  59. data/spec/spec_helper.rb +4 -2
  60. data/spec/unit/chef_runner_spec.rb +12 -5
  61. data/spec/unit/cli_spec.rb +4 -4
  62. data/spec/unit/command/base_spec.rb +1 -1
  63. data/spec/unit/command/exec_spec.rb +37 -27
  64. data/spec/unit/command/generate_spec.rb +3 -3
  65. data/spec/unit/command/generator_commands/app_spec.rb +131 -0
  66. data/spec/unit/command/generator_commands/attribute_spec.rb +32 -0
  67. data/spec/unit/command/generator_commands/cookbook_file_spec.rb +32 -0
  68. data/spec/unit/command/generator_commands/cookbook_spec.rb +205 -0
  69. data/spec/unit/command/generator_commands/lwrp_spec.rb +32 -0
  70. data/spec/unit/command/generator_commands/recipe_spec.rb +32 -0
  71. data/spec/unit/command/generator_commands/repo_spec.rb +287 -0
  72. data/spec/unit/command/generator_commands/template_spec.rb +32 -0
  73. data/spec/unit/command/shell_init_spec.rb +4 -4
  74. data/spec/unit/command/verify_spec.rb +9 -9
  75. data/spec/unit/commands_map_spec.rb +1 -1
  76. data/spec/unit/component_test_spec.rb +3 -3
  77. data/spec/unit/cookbook_profiler/git_spec.rb +7 -7
  78. data/spec/unit/cookbook_profiler/identifiers_spec.rb +12 -8
  79. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/Berksfile +1 -1
  80. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/Berksfile +1 -1
  81. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/Berksfile +1 -1
  82. data/spec/unit/fixtures/example_cookbook/Berksfile +1 -1
  83. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/README.md +4 -0
  84. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/chefignore +96 -0
  85. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/metadata.rb +8 -0
  86. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/recipes/default.rb +8 -0
  87. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/Berksfile +1 -1
  88. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/.kitchen.yml +16 -0
  89. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/README.md +4 -0
  90. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/metadata.rb +8 -0
  91. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/recipes/default.rb +8 -0
  92. data/spec/unit/generator_spec.rb +120 -0
  93. data/spec/unit/policyfile/{cookbook_spec_spec.rb → cookbook_location_specification_spec.rb} +83 -38
  94. data/spec/unit/policyfile/cookbook_locks_spec.rb +354 -0
  95. data/spec/unit/policyfile/read_cookbook_for_compat_mode_upload_spec.rb +85 -0
  96. data/spec/unit/policyfile/solution_dependencies_spec.rb +145 -0
  97. data/spec/unit/policyfile/storage_config_spec.rb +98 -0
  98. data/spec/unit/policyfile/uploader_spec.rb +292 -0
  99. data/spec/unit/policyfile_demands_spec.rb +177 -24
  100. data/spec/unit/policyfile_evaluation_spec.rb +40 -12
  101. data/spec/unit/{policyfile_builder_spec.rb → policyfile_lock_build_spec.rb} +179 -64
  102. data/spec/unit/policyfile_lock_install_spec.rb +138 -0
  103. data/spec/unit/policyfile_lock_validation_spec.rb +610 -0
  104. metadata +103 -59
  105. data/spec/unit/command/generator_commands_spec.rb +0 -504
@@ -0,0 +1,70 @@
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/cookbook/cookbook_version_loader'
19
+
20
+ require 'chef/cookbook/chefignore'
21
+
22
+ module ChefDK
23
+ module Policyfile
24
+ class ReadCookbookForCompatModeUpload
25
+
26
+ # Convenience method to load a cookbook, set up name and version overrides
27
+ # as necessary, and return a Chef::CookbookVersion object.
28
+ def self.load(name, version_override, directory_path)
29
+ new(name, version_override, directory_path).cookbook_version
30
+ end
31
+
32
+ attr_reader :cookbook_name
33
+ attr_reader :directory_path
34
+ attr_reader :version_override
35
+
36
+ def initialize(cookbook_name, version_override, directory_path)
37
+ @cookbook_name = cookbook_name
38
+ @version_override = version_override
39
+ @directory_path = directory_path
40
+
41
+ @cookbook_version = nil
42
+ @loader = nil
43
+ end
44
+
45
+ def cookbook_version
46
+ @cookbook_version ||=
47
+ begin
48
+ cookbook_version = loader.cookbook_version
49
+ cookbook_version.version = version_override
50
+ cookbook_version.freeze_version
51
+ cookbook_version
52
+ end
53
+ end
54
+
55
+ def loader
56
+ @loader ||=
57
+ begin
58
+ cbvl = Chef::Cookbook::CookbookVersionLoader.new(directory_path, chefignore)
59
+ cbvl.load!
60
+ cbvl
61
+ end
62
+ end
63
+
64
+ def chefignore
65
+ @chefignore ||= Chef::Cookbook::Chefignore.new(File.join(directory_path, "chefignore"))
66
+ end
67
+
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,204 @@
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
+
20
+ require 'chef-dk/exceptions'
21
+
22
+ module ChefDK
23
+ module Policyfile
24
+
25
+ class SolutionDependencies
26
+
27
+ Cookbook = Struct.new(:name, :version)
28
+
29
+ class Cookbook
30
+
31
+ def self.parse(str)
32
+ name, version_w_parens = str.split(' ')
33
+ version = version_w_parens[/\(([^)]+)\)/, 1]
34
+ new(name, version)
35
+ end
36
+
37
+ def to_s
38
+ "#{name} (#{version})"
39
+ end
40
+
41
+ def eql?(other)
42
+ other.kind_of?(self.class) and
43
+ other.name == name and
44
+ other.version == version
45
+ end
46
+
47
+ def hash
48
+ [name, version].hash
49
+ end
50
+
51
+ end
52
+
53
+ def self.from_lock(lock_data)
54
+ new.tap {|e| e.consume_lock_data(lock_data) }
55
+ end
56
+
57
+ attr_reader :policyfile_dependencies
58
+
59
+ attr_reader :cookbook_dependencies
60
+
61
+ def initialize
62
+ @policyfile_dependencies = []
63
+ @cookbook_dependencies = {}
64
+ end
65
+
66
+ def add_policyfile_dep(cookbook, constraint)
67
+ @policyfile_dependencies << [ cookbook, Semverse::Constraint.new(constraint) ]
68
+ end
69
+
70
+ def add_cookbook_dep(cookbook_name, version, dependency_list)
71
+ cookbook = Cookbook.new(cookbook_name, version)
72
+ add_cookbook_obj_dep(cookbook, dependency_list)
73
+ end
74
+
75
+ def update_cookbook_dep(cookbook_name, new_version, new_dependency_list)
76
+ @cookbook_dependencies.delete_if { |cb, _deps| cb.name == cookbook_name }
77
+ add_cookbook_dep(cookbook_name, new_version, new_dependency_list)
78
+ end
79
+
80
+ def consume_lock_data(lock_data)
81
+ policyfile_dependencies_data = lock_data["Policyfile"] || []
82
+ policyfile_dependencies_data.each do |cookbook_name, constraint|
83
+ add_policyfile_dep(cookbook_name, constraint)
84
+ end
85
+ cookbook_dependencies_data = lock_data["dependencies"] || {}
86
+ cookbook_dependencies_data.each do |name_and_version, deps_list|
87
+ cookbook = Cookbook.parse(name_and_version)
88
+ add_cookbook_obj_dep(cookbook, deps_list)
89
+ end
90
+ end
91
+
92
+ def test_conflict!(cookbook_name, version)
93
+ unless have_cookbook_dep?(cookbook_name, version)
94
+ raise CookbookNotInWorkingSet, "Cookbook #{cookbook_name} (#{version}) not in the working set, cannot test for conflicts"
95
+ end
96
+
97
+ assert_cookbook_version_valid!(cookbook_name, version)
98
+ assert_cookbook_deps_valid!(cookbook_name, version)
99
+ end
100
+
101
+ def to_lock
102
+ { "Policyfile" => policyfile_dependencies_for_lock, "dependencies" => cookbook_deps_for_lock }
103
+ end
104
+
105
+ def policyfile_dependencies_for_lock
106
+ policyfile_dependencies.map do |name, constraint|
107
+ [ name, constraint.to_s ]
108
+ end
109
+ end
110
+
111
+ def cookbook_deps_for_lock
112
+ cookbook_dependencies.inject({}) do |map, (cookbook, deps)|
113
+ map[cookbook.to_s] = deps.map do |name, constraint|
114
+ [ name, constraint.to_s ]
115
+ end
116
+ map
117
+ end
118
+ end
119
+
120
+ private
121
+
122
+ def add_cookbook_obj_dep(cookbook, dependency_map)
123
+ @cookbook_dependencies[cookbook] = dependency_map.map do |dep_name, constraint|
124
+ [ dep_name, Semverse::Constraint.new(constraint) ]
125
+ end
126
+ end
127
+
128
+ def assert_cookbook_version_valid!(cookbook_name, version)
129
+ policyfile_conflicts = policyfile_conflicts_with(cookbook_name, version)
130
+ cookbook_conflicts = cookbook_conflicts_with(cookbook_name, version)
131
+ all_conflicts = policyfile_conflicts + cookbook_conflicts
132
+
133
+ return false if all_conflicts.empty?
134
+
135
+ details = all_conflicts.map { |source, name, constraint| "#{source} depends on #{name} #{constraint}" }
136
+ message = "Cookbook #{cookbook_name} (#{version}) conflicts with other dependencies:\n"
137
+ full_message = message + details.join("\n")
138
+ raise DependencyConflict, full_message
139
+ end
140
+
141
+ def assert_cookbook_deps_valid!(cookbook_name, version)
142
+ dependency_conflicts = cookbook_deps_conflicts_for(cookbook_name, version)
143
+ return false if dependency_conflicts.empty?
144
+ message = "Cookbook #{cookbook_name} (#{version}) has dependency constraints that cannot be met by the existing cookbook set:\n"
145
+ full_message = message + dependency_conflicts.join("\n")
146
+ raise DependencyConflict, full_message
147
+ end
148
+
149
+ def policyfile_conflicts_with(cookbook_name, version)
150
+ policyfile_conflicts = []
151
+
152
+ @policyfile_dependencies.each do |dep_name, constraint|
153
+ if dep_name == cookbook_name and !constraint.satisfies?(version)
154
+ policyfile_conflicts << ['Policyfile', dep_name, constraint]
155
+ end
156
+ end
157
+
158
+ policyfile_conflicts
159
+ end
160
+
161
+ def cookbook_conflicts_with(cookbook_name, version)
162
+ cookbook_conflicts = []
163
+
164
+ @cookbook_dependencies.each do |top_level_dep_name, dependencies|
165
+ dependencies.each do |dep_name, constraint|
166
+ if dep_name == cookbook_name and !constraint.satisfies?(version)
167
+ cookbook_conflicts << [top_level_dep_name, dep_name, constraint]
168
+ end
169
+ end
170
+ end
171
+
172
+ cookbook_conflicts
173
+ end
174
+
175
+ def cookbook_deps_conflicts_for(cookbook_name, version)
176
+ conflicts = []
177
+ transitive_deps = find_cookbook_dep_by_name_and_version(cookbook_name, version)
178
+ transitive_deps.each do |name, constraint|
179
+ existing_cookbook = find_cookbook_dep_by_name(name)
180
+ if existing_cookbook.nil?
181
+ conflicts << "Cookbook #{name} isn't included in the existing cookbook set."
182
+ elsif !constraint.satisfies?(existing_cookbook[0].version)
183
+ conflicts << "Dependency on #{name} #{constraint} conflicts with existing version #{existing_cookbook[0]}"
184
+ end
185
+ end
186
+ conflicts
187
+ end
188
+
189
+ def have_cookbook_dep?(name, version)
190
+ @cookbook_dependencies.key?(Cookbook.new(name, version))
191
+ end
192
+
193
+ def find_cookbook_dep_by_name(name)
194
+ @cookbook_dependencies.find { |k,v| k.name == name }
195
+ end
196
+
197
+ def find_cookbook_dep_by_name_and_version(name, version)
198
+ @cookbook_dependencies[Cookbook.new(name, version)]
199
+ end
200
+
201
+ end
202
+
203
+ end
204
+ end
@@ -0,0 +1,77 @@
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/cookbook_omnifetch'
19
+
20
+ module ChefDK
21
+ module Policyfile
22
+
23
+ class StorageConfig
24
+
25
+ attr_accessor :relative_paths_root
26
+ attr_accessor :cache_path
27
+
28
+ attr_reader :policyfile_filename
29
+ attr_reader :policyfile_lock_filename
30
+
31
+ def initialize(options = {})
32
+ @relative_paths_root = Dir.pwd
33
+ @cache_path = CookbookOmnifetch.storage_path
34
+ @policyfile_filename = "<< Policyfile filename not specified >>"
35
+ @policyfile_lock_filename = "<< Policyfile lock filename not specified >>"
36
+ handle_options(options)
37
+ end
38
+
39
+ def use_policyfile(policyfile_filename)
40
+ @policyfile_filename = policyfile_filename
41
+ @relative_paths_root = File.dirname(policyfile_filename)
42
+ self
43
+ end
44
+
45
+ def use_policyfile_lock(policyfile_lock_filename)
46
+ @policyfile_lock_filename = policyfile_lock_filename
47
+ @relative_paths_root = File.dirname(policyfile_lock_filename)
48
+ self
49
+ end
50
+
51
+ private
52
+
53
+ def handle_options(options)
54
+ @cache_path = options[:cache_path] if options[:cache_path]
55
+ @relative_paths_root = options[:relative_paths_root] if options.key?(:relative_paths_root)
56
+ end
57
+ end
58
+
59
+ module StorageConfigDelegation
60
+
61
+ def cache_path
62
+ storage_config.cache_path
63
+ end
64
+
65
+ def relative_paths_root
66
+ storage_config.relative_paths_root
67
+ end
68
+
69
+ def policyfile_filename
70
+ storage_config.policyfile_filename
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+ end
77
+
@@ -0,0 +1,110 @@
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/cookbook_uploader'
19
+ require 'chef-dk/policyfile/read_cookbook_for_compat_mode_upload'
20
+
21
+ module ChefDK
22
+ module Policyfile
23
+ class Uploader
24
+
25
+ COMPAT_MODE_DATA_BAG_NAME = "policyfiles".freeze
26
+
27
+ attr_reader :policyfile_lock
28
+ attr_reader :policy_group
29
+ attr_reader :http_client
30
+
31
+ def initialize(policyfile_lock, policy_group, http_client: nil)
32
+ @policyfile_lock = policyfile_lock
33
+ @policy_group = policy_group
34
+ @http_client = http_client
35
+ end
36
+
37
+ def upload
38
+ uploader.upload_cookbooks
39
+ data_bag_create
40
+ data_bag_item_create
41
+ end
42
+
43
+ def data_bag_create
44
+ http_client.post("data", {"name" => COMPAT_MODE_DATA_BAG_NAME})
45
+ rescue Net::HTTPServerException => e
46
+ raise e unless e.response.code == "409"
47
+ end
48
+
49
+ def data_bag_item_create
50
+ policy_id = "#{policyfile_lock.name}-#{policy_group}"
51
+ lock_data = policyfile_lock.to_lock.dup
52
+
53
+ lock_data["id"] = policy_id
54
+
55
+ data_item = {
56
+ "id" => policy_id,
57
+ "name" => "data_bag_item_#{COMPAT_MODE_DATA_BAG_NAME}_#{policy_id}",
58
+ "data_bag" => COMPAT_MODE_DATA_BAG_NAME,
59
+ "raw_data" => lock_data,
60
+ # we'd prefer to leave this out, but the "compatibility mode"
61
+ # implementation in chef-client relies on magical class inflation
62
+ "json_class" => "Chef::DataBagItem"
63
+ }
64
+
65
+ upload_lockfile_as_data_bag_item(policy_id, data_item)
66
+ end
67
+
68
+ def uploader
69
+ # TODO: uploader runs cookbook validation; we want to do this at a different time.
70
+ @uploader ||= Chef::CookbookUploader.new(cookbook_versions_to_upload, :rest => http_client)
71
+ end
72
+
73
+ def cookbook_versions_to_upload
74
+ cookbook_versions_for_policy.reject do |cookbook|
75
+ remote_already_has_cookbook?(cookbook)
76
+ end
77
+ end
78
+
79
+ def remote_already_has_cookbook?(cookbook)
80
+ return false unless existing_cookbook_on_remote.key?(cookbook.name.to_s)
81
+
82
+ existing_cookbook_on_remote[cookbook.name.to_s]["versions"].any? do |cookbook_info|
83
+ cookbook_info["version"] == cookbook.version
84
+ end
85
+ end
86
+
87
+ def existing_cookbook_on_remote
88
+ @existing_cookbook_on_remote ||= http_client.get('cookbooks?num_versions=all')
89
+ end
90
+
91
+ # An Array of Chef::CookbookVersion objects representing the full set that
92
+ # the policyfile lock requires.
93
+ def cookbook_versions_for_policy
94
+ policyfile_lock.validate_cookbooks!
95
+ policyfile_lock.cookbook_locks.map do |name, lock|
96
+ ReadCookbookForCompatModeUpload.load(name, lock.dotted_decimal_identifier, lock.cookbook_path)
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def upload_lockfile_as_data_bag_item(policy_id, data_item)
103
+ http_client.put("data/#{COMPAT_MODE_DATA_BAG_NAME}/#{policy_id}", data_item)
104
+ rescue Net::HTTPServerException => e
105
+ raise e unless e.response.code == "404"
106
+ http_client.post("data/#{COMPAT_MODE_DATA_BAG_NAME}", data_item)
107
+ end
108
+ end
109
+ end
110
+ end