chef-dk 3.9.0 → 3.10.1

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 (377) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +139 -139
  3. data/Gemfile.lock +917 -919
  4. data/LICENSE +201 -201
  5. data/Rakefile +77 -77
  6. data/bin/chef +25 -25
  7. data/chef-dk.gemspec +60 -60
  8. data/lib/chef-dk.rb +19 -19
  9. data/lib/chef-dk/authenticated_http.rb +22 -22
  10. data/lib/chef-dk/builtin_commands.rb +62 -62
  11. data/lib/chef-dk/chef_runner.rb +114 -114
  12. data/lib/chef-dk/chef_server_api_multi.rb +73 -73
  13. data/lib/chef-dk/cli.rb +201 -201
  14. data/lib/chef-dk/command/base.rb +79 -79
  15. data/lib/chef-dk/command/clean_policy_cookbooks.rb +114 -114
  16. data/lib/chef-dk/command/clean_policy_revisions.rb +111 -111
  17. data/lib/chef-dk/command/delete_policy.rb +120 -120
  18. data/lib/chef-dk/command/delete_policy_group.rb +120 -120
  19. data/lib/chef-dk/command/describe_cookbook.rb +95 -95
  20. data/lib/chef-dk/command/diff.rb +315 -315
  21. data/lib/chef-dk/command/env.rb +89 -89
  22. data/lib/chef-dk/command/exec.rb +44 -44
  23. data/lib/chef-dk/command/export.rb +155 -155
  24. data/lib/chef-dk/command/gem.rb +47 -47
  25. data/lib/chef-dk/command/generate.rb +126 -126
  26. data/lib/chef-dk/command/generator_commands.rb +83 -83
  27. data/lib/chef-dk/command/generator_commands/app.rb +106 -106
  28. data/lib/chef-dk/command/generator_commands/attribute.rb +36 -36
  29. data/lib/chef-dk/command/generator_commands/base.rb +157 -157
  30. data/lib/chef-dk/command/generator_commands/build_cookbook.rb +125 -125
  31. data/lib/chef-dk/command/generator_commands/chef_exts/generator_desc_resource.rb +85 -85
  32. data/lib/chef-dk/command/generator_commands/chef_exts/quieter_doc_formatter.rb +38 -38
  33. data/lib/chef-dk/command/generator_commands/chef_exts/recipe_dsl_ext.rb +39 -39
  34. data/lib/chef-dk/command/generator_commands/cookbook.rb +251 -251
  35. data/lib/chef-dk/command/generator_commands/cookbook_code_file.rb +100 -100
  36. data/lib/chef-dk/command/generator_commands/cookbook_file.rb +45 -45
  37. data/lib/chef-dk/command/generator_commands/generator_generator.rb +174 -174
  38. data/lib/chef-dk/command/generator_commands/helpers.rb +36 -36
  39. data/lib/chef-dk/command/generator_commands/policyfile.rb +124 -124
  40. data/lib/chef-dk/command/generator_commands/recipe.rb +36 -36
  41. data/lib/chef-dk/command/generator_commands/repo.rb +123 -123
  42. data/lib/chef-dk/command/generator_commands/resource.rb +36 -36
  43. data/lib/chef-dk/command/generator_commands/template.rb +46 -46
  44. data/lib/chef-dk/command/install.rb +120 -120
  45. data/lib/chef-dk/command/provision.rb +439 -439
  46. data/lib/chef-dk/command/push.rb +117 -117
  47. data/lib/chef-dk/command/push_archive.rb +125 -125
  48. data/lib/chef-dk/command/shell_init.rb +179 -179
  49. data/lib/chef-dk/command/show_policy.rb +163 -163
  50. data/lib/chef-dk/command/undelete.rb +154 -154
  51. data/lib/chef-dk/command/update.rb +139 -139
  52. data/lib/chef-dk/command/verify.rb +638 -638
  53. data/lib/chef-dk/commands_map.rb +113 -113
  54. data/lib/chef-dk/completions/bash.sh.erb +5 -5
  55. data/lib/chef-dk/completions/chef.fish.erb +10 -10
  56. data/lib/chef-dk/completions/zsh.zsh.erb +21 -21
  57. data/lib/chef-dk/component_test.rb +227 -227
  58. data/lib/chef-dk/configurable.rb +88 -88
  59. data/lib/chef-dk/cookbook_metadata.rb +45 -45
  60. data/lib/chef-dk/cookbook_omnifetch.rb +32 -32
  61. data/lib/chef-dk/cookbook_profiler/git.rb +152 -152
  62. data/lib/chef-dk/cookbook_profiler/identifiers.rb +72 -72
  63. data/lib/chef-dk/cookbook_profiler/null_scm.rb +31 -31
  64. data/lib/chef-dk/exceptions.rb +151 -151
  65. data/lib/chef-dk/generator.rb +165 -165
  66. data/lib/chef-dk/helpers.rb +176 -176
  67. data/lib/chef-dk/pager.rb +104 -104
  68. data/lib/chef-dk/policyfile/artifactory_cookbook_source.rb +102 -102
  69. data/lib/chef-dk/policyfile/attribute_merge_checker.rb +110 -110
  70. data/lib/chef-dk/policyfile/chef_repo_cookbook_source.rb +138 -138
  71. data/lib/chef-dk/policyfile/chef_server_cookbook_source.rb +99 -99
  72. data/lib/chef-dk/policyfile/chef_server_lock_fetcher.rb +167 -167
  73. data/lib/chef-dk/policyfile/community_cookbook_source.rb +95 -95
  74. data/lib/chef-dk/policyfile/comparison_base.rb +123 -123
  75. data/lib/chef-dk/policyfile/cookbook_location_specification.rb +154 -154
  76. data/lib/chef-dk/policyfile/cookbook_locks.rb +466 -466
  77. data/lib/chef-dk/policyfile/cookbook_sources.rb +23 -23
  78. data/lib/chef-dk/policyfile/delivery_supermarket_source.rb +89 -89
  79. data/lib/chef-dk/policyfile/differ.rb +263 -263
  80. data/lib/chef-dk/policyfile/dsl.rb +288 -288
  81. data/lib/chef-dk/policyfile/git_lock_fetcher.rb +265 -265
  82. data/lib/chef-dk/policyfile/included_policies_cookbook_source.rb +156 -156
  83. data/lib/chef-dk/policyfile/lister.rb +229 -229
  84. data/lib/chef-dk/policyfile/local_lock_fetcher.rb +132 -129
  85. data/lib/chef-dk/policyfile/lock_applier.rb +80 -80
  86. data/lib/chef-dk/policyfile/lock_fetcher_mixin.rb +37 -0
  87. data/lib/chef-dk/policyfile/null_cookbook_source.rb +49 -49
  88. data/lib/chef-dk/policyfile/policyfile_location_specification.rb +128 -125
  89. data/lib/chef-dk/policyfile/read_cookbook_for_compat_mode_upload.rb +124 -124
  90. data/lib/chef-dk/policyfile/remote_lock_fetcher.rb +108 -0
  91. data/lib/chef-dk/policyfile/reports/install.rb +69 -69
  92. data/lib/chef-dk/policyfile/reports/table_printer.rb +57 -57
  93. data/lib/chef-dk/policyfile/reports/upload.rb +70 -70
  94. data/lib/chef-dk/policyfile/solution_dependencies.rb +311 -311
  95. data/lib/chef-dk/policyfile/source_uri.rb +57 -57
  96. data/lib/chef-dk/policyfile/storage_config.rb +112 -112
  97. data/lib/chef-dk/policyfile/undo_record.rb +139 -139
  98. data/lib/chef-dk/policyfile/undo_stack.rb +128 -128
  99. data/lib/chef-dk/policyfile/uploader.rb +222 -222
  100. data/lib/chef-dk/policyfile_compiler.rb +528 -528
  101. data/lib/chef-dk/policyfile_lock.rb +581 -581
  102. data/lib/chef-dk/policyfile_services/clean_policies.rb +95 -95
  103. data/lib/chef-dk/policyfile_services/clean_policy_cookbooks.rb +123 -123
  104. data/lib/chef-dk/policyfile_services/export_repo.rb +419 -419
  105. data/lib/chef-dk/policyfile_services/install.rb +167 -167
  106. data/lib/chef-dk/policyfile_services/push.rb +112 -112
  107. data/lib/chef-dk/policyfile_services/push_archive.rb +164 -164
  108. data/lib/chef-dk/policyfile_services/rm_policy.rb +141 -141
  109. data/lib/chef-dk/policyfile_services/rm_policy_group.rb +85 -85
  110. data/lib/chef-dk/policyfile_services/show_policy.rb +234 -234
  111. data/lib/chef-dk/policyfile_services/undelete.rb +108 -108
  112. data/lib/chef-dk/policyfile_services/update_attributes.rb +110 -110
  113. data/lib/chef-dk/service_exception_inspectors.rb +24 -24
  114. data/lib/chef-dk/service_exception_inspectors/base.rb +39 -39
  115. data/lib/chef-dk/service_exception_inspectors/http.rb +119 -119
  116. data/lib/chef-dk/service_exceptions.rb +142 -142
  117. data/lib/chef-dk/shell_out.rb +36 -36
  118. data/lib/chef-dk/skeletons/code_generator/files/default/Berksfile +4 -4
  119. data/lib/chef-dk/skeletons/code_generator/files/default/build_cookbook/.kitchen.yml +21 -21
  120. data/lib/chef-dk/skeletons/code_generator/files/default/build_cookbook/README.md +146 -146
  121. data/lib/chef-dk/skeletons/code_generator/files/default/build_cookbook/test-fixture-recipe.rb +9 -9
  122. data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +104 -104
  123. data/lib/chef-dk/skeletons/code_generator/files/default/cookbook_readmes/README-policy.md +9 -9
  124. data/lib/chef-dk/skeletons/code_generator/files/default/cookbook_readmes/README.md +66 -66
  125. data/lib/chef-dk/skeletons/code_generator/files/default/delivery-config.json +17 -17
  126. data/lib/chef-dk/skeletons/code_generator/files/default/delivery-project.toml +36 -36
  127. data/lib/chef-dk/skeletons/code_generator/files/default/gitignore +22 -22
  128. data/lib/chef-dk/skeletons/code_generator/files/default/repo/README.md +24 -24
  129. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/README.md +27 -27
  130. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/attributes/default.rb +8 -8
  131. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/metadata.rb +7 -7
  132. data/lib/chef-dk/skeletons/code_generator/files/default/repo/cookbooks/example/recipes/default.rb +9 -9
  133. data/lib/chef-dk/skeletons/code_generator/files/default/repo/data_bags/README.md +56 -56
  134. data/lib/chef-dk/skeletons/code_generator/files/default/repo/data_bags/example/example_item.json +3 -3
  135. data/lib/chef-dk/skeletons/code_generator/files/default/repo/dot-chef-repo.txt +6 -6
  136. data/lib/chef-dk/skeletons/code_generator/files/default/repo/environments/README.md +9 -9
  137. data/lib/chef-dk/skeletons/code_generator/files/default/repo/environments/example.json +12 -12
  138. data/lib/chef-dk/skeletons/code_generator/files/default/repo/policyfiles/README.md +24 -24
  139. data/lib/chef-dk/skeletons/code_generator/files/default/repo/roles/README.md +9 -9
  140. data/lib/chef-dk/skeletons/code_generator/files/default/repo/roles/example.json +12 -12
  141. data/lib/chef-dk/skeletons/code_generator/files/default/spec_helper.rb +3 -3
  142. data/lib/chef-dk/skeletons/code_generator/files/default/spec_helper_policyfile.rb +3 -3
  143. data/lib/chef-dk/skeletons/code_generator/metadata.rb +8 -8
  144. data/lib/chef-dk/skeletons/code_generator/recipes/app.rb +89 -89
  145. data/lib/chef-dk/skeletons/code_generator/recipes/attribute.rb +13 -13
  146. data/lib/chef-dk/skeletons/code_generator/recipes/build_cookbook.rb +177 -177
  147. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook.rb +161 -161
  148. data/lib/chef-dk/skeletons/code_generator/recipes/cookbook_file.rb +25 -25
  149. data/lib/chef-dk/skeletons/code_generator/recipes/helpers.rb +21 -21
  150. data/lib/chef-dk/skeletons/code_generator/recipes/policyfile.rb +9 -9
  151. data/lib/chef-dk/skeletons/code_generator/recipes/recipe.rb +52 -52
  152. data/lib/chef-dk/skeletons/code_generator/recipes/repo.rb +68 -68
  153. data/lib/chef-dk/skeletons/code_generator/recipes/resource.rb +13 -13
  154. data/lib/chef-dk/skeletons/code_generator/recipes/template.rb +32 -32
  155. data/lib/chef-dk/skeletons/code_generator/templates/default/CHANGELOG.md.erb +11 -11
  156. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.all_rights.erb +3 -3
  157. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.apachev2.erb +201 -201
  158. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.gplv2.erb +339 -339
  159. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.gplv3.erb +674 -674
  160. data/lib/chef-dk/skeletons/code_generator/templates/default/LICENSE.mit.erb +21 -21
  161. data/lib/chef-dk/skeletons/code_generator/templates/default/Policyfile.rb.erb +25 -25
  162. data/lib/chef-dk/skeletons/code_generator/templates/default/README.md.erb +4 -4
  163. data/lib/chef-dk/skeletons/code_generator/templates/default/attribute.rb.erb +0 -0
  164. data/lib/chef-dk/skeletons/code_generator/templates/default/build_cookbook/Berksfile.erb +7 -7
  165. data/lib/chef-dk/skeletons/code_generator/templates/default/build_cookbook/metadata.rb.erb +10 -10
  166. data/lib/chef-dk/skeletons/code_generator/templates/default/build_cookbook/recipe.rb.erb +8 -8
  167. data/lib/chef-dk/skeletons/code_generator/templates/default/cookbook_file.erb +0 -0
  168. data/lib/chef-dk/skeletons/code_generator/templates/default/helpers.rb.erb +39 -39
  169. data/lib/chef-dk/skeletons/code_generator/templates/default/inspec_default_test.rb.erb +16 -16
  170. data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen.yml.erb +26 -26
  171. data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen_dokken.yml.erb +31 -31
  172. data/lib/chef-dk/skeletons/code_generator/templates/default/kitchen_policyfile.yml.erb +33 -33
  173. data/lib/chef-dk/skeletons/code_generator/templates/default/metadata.rb.erb +20 -20
  174. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe.rb.erb +5 -5
  175. data/lib/chef-dk/skeletons/code_generator/templates/default/recipe_spec.rb.erb +35 -35
  176. data/lib/chef-dk/skeletons/code_generator/templates/default/repo/gitignore.erb +128 -128
  177. data/lib/chef-dk/skeletons/code_generator/templates/default/resource.rb.erb +1 -1
  178. data/lib/chef-dk/skeletons/code_generator/templates/default/template.erb +0 -0
  179. data/lib/chef-dk/ui.rb +57 -57
  180. data/lib/chef-dk/version.rb +20 -20
  181. data/lib/kitchen/provisioner/policyfile_zero.rb +195 -195
  182. data/spec/shared/a_file_generator.rb +125 -125
  183. data/spec/shared/a_generated_file.rb +12 -12
  184. data/spec/shared/command_with_ui_object.rb +11 -11
  185. data/spec/shared/custom_generator_cookbook.rb +136 -136
  186. data/spec/shared/fixture_cookbook_checksums.rb +46 -46
  187. data/spec/shared/setup_git_committer_config.rb +54 -54
  188. data/spec/shared/setup_git_cookbooks.rb +53 -53
  189. data/spec/spec_helper.rb +51 -51
  190. data/spec/test_helpers.rb +84 -84
  191. data/spec/unit/chef_runner_spec.rb +139 -139
  192. data/spec/unit/chef_server_api_multi_spec.rb +120 -120
  193. data/spec/unit/cli_spec.rb +377 -377
  194. data/spec/unit/command/base_spec.rb +172 -172
  195. data/spec/unit/command/clean_policy_cookbooks_spec.rb +180 -180
  196. data/spec/unit/command/clean_policy_revisions_spec.rb +180 -180
  197. data/spec/unit/command/delete_policy_group_spec.rb +206 -206
  198. data/spec/unit/command/delete_policy_spec.rb +206 -206
  199. data/spec/unit/command/diff_spec.rb +311 -311
  200. data/spec/unit/command/env_spec.rb +52 -52
  201. data/spec/unit/command/exec_spec.rb +178 -178
  202. data/spec/unit/command/export_spec.rb +199 -199
  203. data/spec/unit/command/generate_spec.rb +142 -142
  204. data/spec/unit/command/generator_commands/app_spec.rb +166 -166
  205. data/spec/unit/command/generator_commands/attribute_spec.rb +31 -31
  206. data/spec/unit/command/generator_commands/base_spec.rb +181 -181
  207. data/spec/unit/command/generator_commands/build_cookbook_spec.rb +377 -377
  208. data/spec/unit/command/generator_commands/chef_exts/generator_desc_resource_spec.rb +97 -97
  209. data/spec/unit/command/generator_commands/chef_exts/recipe_dsl_ext_spec.rb +111 -111
  210. data/spec/unit/command/generator_commands/cookbook_file_spec.rb +31 -31
  211. data/spec/unit/command/generator_commands/cookbook_spec.rb +765 -765
  212. data/spec/unit/command/generator_commands/generator_generator_spec.rb +227 -227
  213. data/spec/unit/command/generator_commands/helpers_spec.rb +31 -31
  214. data/spec/unit/command/generator_commands/policyfile_spec.rb +223 -223
  215. data/spec/unit/command/generator_commands/recipe_spec.rb +37 -37
  216. data/spec/unit/command/generator_commands/repo_spec.rb +374 -374
  217. data/spec/unit/command/generator_commands/resource_spec.rb +31 -31
  218. data/spec/unit/command/generator_commands/template_spec.rb +31 -31
  219. data/spec/unit/command/install_spec.rb +179 -179
  220. data/spec/unit/command/provision_spec.rb +589 -589
  221. data/spec/unit/command/push_archive_spec.rb +153 -153
  222. data/spec/unit/command/push_spec.rb +198 -198
  223. data/spec/unit/command/shell_init_spec.rb +339 -339
  224. data/spec/unit/command/show_policy_spec.rb +234 -234
  225. data/spec/unit/command/undelete_spec.rb +244 -244
  226. data/spec/unit/command/update_spec.rb +283 -283
  227. data/spec/unit/command/verify_spec.rb +342 -342
  228. data/spec/unit/commands_map_spec.rb +57 -57
  229. data/spec/unit/component_test_spec.rb +128 -128
  230. data/spec/unit/configurable_spec.rb +68 -68
  231. data/spec/unit/cookbook_metadata_spec.rb +96 -96
  232. data/spec/unit/cookbook_profiler/git_spec.rb +176 -176
  233. data/spec/unit/cookbook_profiler/identifiers_spec.rb +81 -81
  234. data/spec/unit/fixtures/chef-runner-cookbooks/test_cookbook/recipes/recipe_one.rb +9 -9
  235. data/spec/unit/fixtures/chef-runner-cookbooks/test_cookbook/recipes/recipe_two.rb +9 -9
  236. data/spec/unit/fixtures/command/cli_test_command.rb +26 -26
  237. data/spec/unit/fixtures/command/explicit_path_example.rb +7 -7
  238. data/spec/unit/fixtures/configurable/test_config_loader.rb +5 -5
  239. data/spec/unit/fixtures/configurable/test_configurable.rb +10 -10
  240. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/.kitchen.yml +16 -16
  241. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/Berksfile +3 -3
  242. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/README.md +4 -4
  243. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/chefignore +96 -96
  244. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/metadata.rb +8 -8
  245. data/spec/unit/fixtures/cookbook_cache/baz-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/recipes/default.rb +8 -8
  246. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/.kitchen.yml +16 -16
  247. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/Berksfile +3 -3
  248. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/README.md +4 -4
  249. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/chefignore +96 -96
  250. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/metadata.rb +8 -8
  251. data/spec/unit/fixtures/cookbook_cache/dep_of_bar-1.2.3/recipes/default.rb +8 -8
  252. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/.kitchen.yml +16 -16
  253. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/Berksfile +3 -3
  254. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/README.md +4 -4
  255. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/chefignore +96 -96
  256. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/metadata.rb +8 -8
  257. data/spec/unit/fixtures/cookbook_cache/foo-1.0.0/recipes/default.rb +8 -8
  258. data/spec/unit/fixtures/cookbooks_api/chef_server_universe.json +56 -56
  259. data/spec/unit/fixtures/cookbooks_api/pruned_chef_server_universe.json +30 -30
  260. data/spec/unit/fixtures/cookbooks_api/pruned_small_universe.json +1321 -1321
  261. data/spec/unit/fixtures/cookbooks_api/small_universe.json +2987 -2987
  262. data/spec/unit/fixtures/cookbooks_api/universe.json +1 -1
  263. data/spec/unit/fixtures/cookbooks_api/update_fixtures.rb +33 -33
  264. data/spec/unit/fixtures/dev_cookbooks/README.md +16 -16
  265. data/spec/unit/fixtures/dev_cookbooks/bar-cookbook.gitbundle +0 -0
  266. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/bin/.keep +0 -0
  267. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/embedded/.keep +0 -0
  268. data/spec/unit/fixtures/eg_omnibus_dir/missing_apps/embedded/bin/.keep +0 -0
  269. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/bin/.keep +0 -0
  270. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/apps/berkshelf/.keep +0 -0
  271. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/apps/test-kitchen/.keep +0 -0
  272. data/spec/unit/fixtures/eg_omnibus_dir/missing_component/embedded/bin/.keep +0 -0
  273. data/spec/unit/fixtures/eg_omnibus_dir/valid/bin/.keep +0 -0
  274. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/berkshelf/integration_test +2 -2
  275. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/berkshelf/verify_me +5 -5
  276. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/chef-dk/.keep +0 -0
  277. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/chef/verify_me +3 -3
  278. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/apps/test-kitchen/verify_me +2 -2
  279. data/spec/unit/fixtures/eg_omnibus_dir/valid/embedded/bin/.keep +0 -0
  280. data/spec/unit/fixtures/example_app/Policyfile.rb +0 -0
  281. data/spec/unit/fixtures/example_cookbook/.gitignore +17 -17
  282. data/spec/unit/fixtures/example_cookbook/.kitchen.yml +16 -16
  283. data/spec/unit/fixtures/example_cookbook/Berksfile +3 -3
  284. data/spec/unit/fixtures/example_cookbook/README.md +4 -4
  285. data/spec/unit/fixtures/example_cookbook/chefignore +96 -96
  286. data/spec/unit/fixtures/example_cookbook/metadata.rb +8 -8
  287. data/spec/unit/fixtures/example_cookbook/recipes/default.rb +8 -8
  288. data/spec/unit/fixtures/example_cookbook_metadata_json_only/.gitignore +17 -17
  289. data/spec/unit/fixtures/example_cookbook_metadata_json_only/.kitchen.yml +16 -16
  290. data/spec/unit/fixtures/example_cookbook_metadata_json_only/Berksfile +3 -3
  291. data/spec/unit/fixtures/example_cookbook_metadata_json_only/README.md +4 -4
  292. data/spec/unit/fixtures/example_cookbook_metadata_json_only/chefignore +96 -96
  293. data/spec/unit/fixtures/example_cookbook_metadata_json_only/metadata.json +5 -5
  294. data/spec/unit/fixtures/example_cookbook_metadata_json_only/recipes/default.rb +8 -8
  295. data/spec/unit/fixtures/example_cookbook_no_metadata/.gitignore +17 -17
  296. data/spec/unit/fixtures/example_cookbook_no_metadata/.kitchen.yml +16 -16
  297. data/spec/unit/fixtures/example_cookbook_no_metadata/Berksfile +3 -3
  298. data/spec/unit/fixtures/example_cookbook_no_metadata/README.md +4 -4
  299. data/spec/unit/fixtures/example_cookbook_no_metadata/chefignore +96 -96
  300. data/spec/unit/fixtures/example_cookbook_no_metadata/recipes/default.rb +8 -8
  301. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/README.md +4 -4
  302. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/chefignore +96 -96
  303. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/metadata.rb +8 -8
  304. data/spec/unit/fixtures/local_path_cookbooks/another-local-cookbook/recipes/default.rb +8 -8
  305. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/Berksfile +3 -3
  306. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/README.md +4 -4
  307. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/chefignore +96 -96
  308. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/metadata.rb +9 -9
  309. data/spec/unit/fixtures/local_path_cookbooks/cookbook-with-a-dep/recipes/default.rb +8 -8
  310. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/.kitchen.yml +16 -16
  311. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/Berksfile +3 -3
  312. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/README.md +4 -4
  313. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/chefignore +96 -96
  314. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/extra/extra_file.txt +0 -0
  315. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/metadata.rb +8 -8
  316. data/spec/unit/fixtures/local_path_cookbooks/local-cookbook/recipes/default.rb +8 -8
  317. data/spec/unit/fixtures/local_path_cookbooks/metadata-missing/README.md +2 -2
  318. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/.kitchen.yml +16 -16
  319. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/README.md +4 -4
  320. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/metadata.rb +8 -8
  321. data/spec/unit/fixtures/local_path_cookbooks/noignore-f59ee7a5bca6a4e606b67f7f856b768d847c39bb/recipes/default.rb +8 -8
  322. data/spec/unit/generator_spec.rb +119 -119
  323. data/spec/unit/pager_spec.rb +117 -117
  324. data/spec/unit/policyfile/artifactory_cookbook_source_spec.rb +59 -59
  325. data/spec/unit/policyfile/attribute_merge_checker_spec.rb +80 -80
  326. data/spec/unit/policyfile/chef_repo_cookbook_source_spec.rb +93 -93
  327. data/spec/unit/policyfile/chef_server_cookbook_source_spec.rb +55 -55
  328. data/spec/unit/policyfile/chef_server_lock_fetcher_spec.rb +161 -161
  329. data/spec/unit/policyfile/community_cookbook_source_spec.rb +83 -83
  330. data/spec/unit/policyfile/comparison_base_spec.rb +340 -340
  331. data/spec/unit/policyfile/cookbook_location_specification_spec.rb +347 -347
  332. data/spec/unit/policyfile/cookbook_locks_spec.rb +527 -527
  333. data/spec/unit/policyfile/delivery_supermarket_source_spec.rb +129 -129
  334. data/spec/unit/policyfile/differ_spec.rb +686 -686
  335. data/spec/unit/policyfile/git_lock_fetcher_spec.rb +155 -155
  336. data/spec/unit/policyfile/included_policies_cookbook_source_spec.rb +242 -242
  337. data/spec/unit/policyfile/lister_spec.rb +268 -268
  338. data/spec/unit/policyfile/local_lock_fetcher_spec.rb +199 -173
  339. data/spec/unit/policyfile/lock_applier_spec.rb +100 -100
  340. data/spec/unit/policyfile/lock_fetcher_mixin_spec.rb +60 -0
  341. data/spec/unit/policyfile/null_cookbook_source_spec.rb +34 -34
  342. data/spec/unit/policyfile/read_cookbook_for_compat_mode_upload_spec.rb +92 -92
  343. data/spec/unit/policyfile/remote_lock_fetcher_spec.rb +129 -0
  344. data/spec/unit/policyfile/reports/install_spec.rb +114 -114
  345. data/spec/unit/policyfile/reports/upload_spec.rb +94 -94
  346. data/spec/unit/policyfile/solution_dependencies_spec.rb +170 -170
  347. data/spec/unit/policyfile/source_uri_spec.rb +36 -36
  348. data/spec/unit/policyfile/storage_config_spec.rb +180 -180
  349. data/spec/unit/policyfile/undo_record_spec.rb +258 -258
  350. data/spec/unit/policyfile/undo_stack_spec.rb +265 -265
  351. data/spec/unit/policyfile/uploader_spec.rb +410 -410
  352. data/spec/unit/policyfile_demands_spec.rb +1197 -1197
  353. data/spec/unit/policyfile_evaluation_spec.rb +628 -628
  354. data/spec/unit/policyfile_includes_dsl_spec.rb +220 -159
  355. data/spec/unit/policyfile_includes_spec.rb +720 -720
  356. data/spec/unit/policyfile_install_with_includes_spec.rb +232 -232
  357. data/spec/unit/policyfile_lock_build_spec.rb +1065 -1065
  358. data/spec/unit/policyfile_lock_install_spec.rb +137 -137
  359. data/spec/unit/policyfile_lock_serialization_spec.rb +424 -424
  360. data/spec/unit/policyfile_lock_validation_spec.rb +608 -608
  361. data/spec/unit/policyfile_services/clean_policies_spec.rb +236 -236
  362. data/spec/unit/policyfile_services/clean_policy_cookbooks_spec.rb +272 -272
  363. data/spec/unit/policyfile_services/export_repo_spec.rb +473 -473
  364. data/spec/unit/policyfile_services/install_spec.rb +209 -209
  365. data/spec/unit/policyfile_services/push_archive_spec.rb +359 -359
  366. data/spec/unit/policyfile_services/push_spec.rb +249 -249
  367. data/spec/unit/policyfile_services/rm_policy_group_spec.rb +237 -237
  368. data/spec/unit/policyfile_services/rm_policy_spec.rb +263 -263
  369. data/spec/unit/policyfile_services/show_policy_spec.rb +887 -887
  370. data/spec/unit/policyfile_services/undelete_spec.rb +302 -302
  371. data/spec/unit/policyfile_services/update_attributes_spec.rb +229 -229
  372. data/spec/unit/policyfile_services/update_spec.rb +162 -162
  373. data/spec/unit/service_exception_inspectors/base_spec.rb +41 -41
  374. data/spec/unit/service_exception_inspectors/http_spec.rb +138 -138
  375. data/spec/unit/shell_out_spec.rb +34 -34
  376. data/warning.txt +9 -9
  377. metadata +8 -2
@@ -1,581 +1,581 @@
1
- # -*- coding: UTF-8 -*-
2
- #
3
- # Copyright:: Copyright (c) 2014-2018 Chef Software Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require "digest/sha2"
20
-
21
- require "chef-dk/policyfile/storage_config"
22
- require "chef-dk/policyfile/cookbook_locks"
23
- require "chef-dk/policyfile/solution_dependencies"
24
- require "chef-dk/ui"
25
-
26
- module ChefDK
27
-
28
- class PolicyfileLock
29
-
30
- class InstallReport
31
-
32
- attr_reader :ui
33
- attr_reader :policyfile_lock
34
-
35
- def initialize(ui: nil, policyfile_lock: nil)
36
- @ui = ui
37
- @policyfile_lock = policyfile_lock
38
-
39
- @cookbook_name_width = nil
40
- @cookbook_version_width = nil
41
- end
42
-
43
- def installing_fixed_version_cookbook(cookbook_spec)
44
- verb = cookbook_spec.installed? ? "Using " : "Installing"
45
- ui.msg("#{verb} #{format_fixed_version_cookbook(cookbook_spec)}")
46
- end
47
-
48
- def installing_cookbook(cookbook_lock)
49
- verb = cookbook_lock.installed? ? "Using " : "Installing"
50
- ui.msg("#{verb} #{format_cookbook(cookbook_lock)}")
51
- end
52
-
53
- private
54
-
55
- def format_cookbook(cookbook_lock)
56
- "#{cookbook_lock.name.ljust(cookbook_name_width)} #{cookbook_lock.version.to_s.ljust(cookbook_version_width)}"
57
- end
58
-
59
- def cookbook_name_width
60
- policyfile_lock.cookbook_locks.map { |name, _| name.size }.max
61
- end
62
-
63
- def cookbook_version_width
64
- policyfile_lock.cookbook_locks.map { |_, lock| lock.version.size }.max
65
- end
66
- end
67
-
68
- RUN_LIST_ITEM_FORMAT = /\Arecipe\[[^\s]+::[^\s]+\]\Z/
69
-
70
- def self.build(storage_config)
71
- lock = new(storage_config)
72
- yield lock
73
- lock
74
- end
75
-
76
- def self.build_from_compiler(compiler, storage_config)
77
- lock = new(storage_config)
78
- lock.build_from_compiler(compiler)
79
- lock
80
- end
81
-
82
- include Policyfile::StorageConfigDelegation
83
-
84
- attr_accessor :name
85
- attr_accessor :run_list
86
- attr_accessor :named_run_lists
87
- attr_accessor :default_attributes
88
- attr_accessor :override_attributes
89
-
90
- attr_reader :solution_dependencies
91
-
92
- attr_reader :storage_config
93
-
94
- attr_reader :cookbook_locks
95
-
96
- attr_reader :included_policy_locks
97
-
98
- attr_reader :install_report
99
-
100
- def initialize(storage_config, ui: nil)
101
- @name = nil
102
- @run_list = []
103
- @named_run_lists = {}
104
- @cookbook_locks = {}
105
- @relative_paths_root = Dir.pwd
106
- @storage_config = storage_config
107
- @ui = ui || UI.null
108
-
109
- @default_attributes = {}
110
- @override_attributes = {}
111
-
112
- @solution_dependencies = Policyfile::SolutionDependencies.new
113
-
114
- @included_policy_locks = []
115
-
116
- @install_report = InstallReport.new(ui: @ui, policyfile_lock: self)
117
- end
118
-
119
- def lock_data_for(cookbook_name)
120
- @cookbook_locks[cookbook_name]
121
- end
122
-
123
- def cached_cookbook(name)
124
- cached_cookbook = Policyfile::CachedCookbook.new(name, storage_config)
125
- yield cached_cookbook if block_given?
126
- @cookbook_locks[name] = cached_cookbook
127
- end
128
-
129
- def local_cookbook(name)
130
- local_cookbook = Policyfile::LocalCookbook.new(name, storage_config)
131
- yield local_cookbook if block_given?
132
- @cookbook_locks[name] = local_cookbook
133
- end
134
-
135
- def dependencies
136
- yield solution_dependencies
137
- end
138
-
139
- def to_lock
140
- {}.tap do |lock|
141
- lock["revision_id"] = revision_id
142
- lock["name"] = name
143
- lock["run_list"] = run_list
144
- lock["named_run_lists"] = named_run_lists unless named_run_lists.empty?
145
- lock["included_policy_locks"] = included_policy_locks
146
- lock["cookbook_locks"] = cookbook_locks_for_lockfile
147
- lock["default_attributes"] = default_attributes
148
- lock["override_attributes"] = override_attributes
149
- lock["solution_dependencies"] = solution_dependencies.to_lock
150
- end
151
- end
152
-
153
- # Returns a fingerprint of the PolicyfileLock by computing the SHA1 hash of
154
- # #canonical_revision_string
155
- def revision_id
156
- Digest::SHA256.new.hexdigest(canonical_revision_string)
157
- end
158
-
159
- # Generates a string representation of the lock data in a specialized
160
- # format suitable for generating a checksum of the lock itself. Only data
161
- # that modifies the behavior of a chef-client using the lockfile is
162
- # included in this format; for example, a modification to the source
163
- # options in a `Policyfile.rb` that yields identical code (such as
164
- # switching to a github fork at the same revision) will not cause a change
165
- # in the PolicyfileLock's canonical_revision_string.
166
- #
167
- # This format is intended to be used only for generating an identifier for
168
- # a particular revision of a PolicyfileLock. It should not be used as a
169
- # serialization format, and is not guaranteed to be a stable interface.
170
- def canonical_revision_string
171
- canonical_rev_text = ""
172
-
173
- canonical_rev_text << "name:#{name}\n"
174
-
175
- run_list.each do |item|
176
- canonical_rev_text << "run-list-item:#{item}\n"
177
- end
178
-
179
- named_run_lists.each do |name, run_list|
180
- run_list.each do |item|
181
- canonical_rev_text << "named-run-list:#{name};run-list-item:#{item}\n"
182
- end
183
- end
184
-
185
- cookbook_locks_for_lockfile.each do |name, lock|
186
- canonical_rev_text << "cookbook:#{name};id:#{lock["identifier"]}\n"
187
- end
188
-
189
- canonical_rev_text << "default_attributes:#{canonicalize(default_attributes)}\n"
190
-
191
- canonical_rev_text << "override_attributes:#{canonicalize(override_attributes)}\n"
192
-
193
- canonical_rev_text
194
- end
195
-
196
- def cookbook_locks_for_lockfile
197
- cookbook_locks.inject({}) do |locks_map, (name, location_spec)|
198
- location_spec.validate!
199
- location_spec.gather_profile_data
200
- locks_map[name] = location_spec.to_lock
201
- locks_map
202
- end.sort.to_h
203
- end
204
-
205
- def validate_cookbooks!
206
- cookbook_locks.each do |name, cookbook_lock|
207
- cookbook_lock.validate!
208
- cookbook_lock.refresh!
209
- end
210
-
211
- # Check that versions and dependencies are still valid. First we need to
212
- # refresh the dependency info for everything that has changed, then we
213
- # check that the new versions and dependencies are valid for the working
214
- # set of cookbooks. We can't do this in a single loop because the user
215
- # may have modified two cookbooks such that the versions and constraints
216
- # are only valid when both changes are considered together.
217
- cookbook_locks.each do |name, cookbook_lock|
218
- if cookbook_lock.updated?
219
- solution_dependencies.update_cookbook_dep(name, cookbook_lock.version, cookbook_lock.dependencies)
220
- end
221
- end
222
- cookbook_locks.each do |name, cookbook_lock|
223
- if cookbook_lock.updated?
224
- solution_dependencies.test_conflict!(cookbook_lock.name, cookbook_lock.version)
225
- end
226
- end
227
-
228
- true
229
- end
230
-
231
- def build_from_compiler(compiler)
232
- @name = compiler.name
233
-
234
- @run_list = compiler.normalized_run_list
235
-
236
- @named_run_lists = compiler.normalized_named_run_lists
237
-
238
- compiler.all_cookbook_location_specs.each do |cookbook_name, spec|
239
- if spec.mirrors_canonical_upstream?
240
- cached_cookbook(cookbook_name) do |cached_cb|
241
- cached_cb.cache_key = spec.cache_key
242
- cached_cb.origin = spec.uri
243
- cached_cb.source_options = spec.source_options_for_lock
244
- end
245
- else
246
- local_cookbook(cookbook_name) do |local_cb|
247
- local_cb.source = spec.relative_path
248
- local_cb.source_options = spec.source_options_for_lock
249
- end
250
- end
251
- end
252
-
253
- @default_attributes = compiler.default_attributes
254
- @override_attributes = compiler.override_attributes
255
-
256
- @solution_dependencies = compiler.solution_dependencies
257
-
258
- @included_policy_locks = compiler.included_policies.map do |policy|
259
- {
260
- "name" => policy.name,
261
- "revision_id" => policy.revision_id,
262
- "source_options" => policy.source_options_for_lock,
263
- }
264
- end
265
-
266
- self
267
- end
268
-
269
- def build_from_lock_data(lock_data)
270
- set_name_from_lock_data(lock_data)
271
- set_run_list_from_lock_data(lock_data)
272
- set_named_run_lists_from_lock_data(lock_data)
273
- set_cookbook_locks_from_lock_data(lock_data)
274
- set_attributes_from_lock_data(lock_data)
275
- set_solution_dependencies_from_lock_data(lock_data)
276
- set_included_policy_locks_from_lock_data(lock_data)
277
- self
278
- end
279
-
280
- def build_from_archive(lock_data)
281
- set_name_from_lock_data(lock_data)
282
- set_run_list_from_lock_data(lock_data)
283
- set_named_run_lists_from_lock_data(lock_data)
284
- set_cookbook_locks_as_archives_from_lock_data(lock_data)
285
- set_attributes_from_lock_data(lock_data)
286
- set_solution_dependencies_from_lock_data(lock_data)
287
- set_included_policy_locks_from_lock_data(lock_data)
288
- self
289
- end
290
-
291
- def install_cookbooks
292
- # note: duplicates PolicyfileCompiler#ensure_cache_dir_exists
293
- ensure_cache_dir_exists
294
-
295
- cookbook_locks.each do |cookbook_name, cookbook_lock|
296
- install_report.installing_cookbook(cookbook_lock)
297
- cookbook_lock.install_locked
298
- end
299
- end
300
-
301
- def ensure_cache_dir_exists
302
- # note: duplicates PolicyfileCompiler#ensure_cache_dir_exists
303
- unless File.exist?(cache_path)
304
- FileUtils.mkdir_p(cache_path)
305
- end
306
- end
307
-
308
- private
309
-
310
- # Generates a canonical JSON representation of the attributes. Based on
311
- # http://wiki.laptop.org/go/Canonical_JSON but not quite as strict, yet.
312
- #
313
- # In particular:
314
- # - String encoding stuff isn't normalized
315
- # - We allow floats that fit within the range/precision requirements of
316
- # IEEE 754-2008 binary64 (double precision) numbers.
317
- # - +/- Infinity and NaN are banned, but float/numeric size aren't checked.
318
- # numerics should be in range [-(2**53)+1, (2**53)-1] to comply with
319
- # IEEE 754-2008
320
- #
321
- # Recursive, so absurd nesting levels could cause a SystemError. Invalid
322
- # input will cause an InvalidPolicyfileAttribute exception.
323
- def canonicalize(attributes)
324
- unless attributes.kind_of?(Hash)
325
- raise "Top level attributes must be a Hash (you gave: #{attributes})"
326
- end
327
- canonicalize_elements(attributes)
328
- end
329
-
330
- def canonicalize_elements(item)
331
- case item
332
- when Hash
333
- # Hash keys will sort differently based on the encoding, but after a
334
- # JSON round trip everything will be UTF-8, so we have to normalize the
335
- # keys to UTF-8 first so that the sort order uses the UTF-8 strings.
336
- item_with_normalized_keys = item.inject({}) do |normalized_item, (key, value)|
337
- validate_attr_key(key)
338
- normalized_item[key.encode("utf-8")] = value
339
- normalized_item
340
- end
341
- elements = item_with_normalized_keys.keys.sort.map do |key|
342
- k = '"' << key << '":'
343
- v = canonicalize_elements(item_with_normalized_keys[key])
344
- k << v
345
- end
346
- "{" << elements.join(",") << "}"
347
- when String
348
- '"' << item.encode("utf-8") << '"'
349
- when Array
350
- elements = item.map { |i| canonicalize_elements(i) }
351
- "[" << elements.join(",") << "]"
352
- when Integer
353
- item.to_s
354
- when Float
355
- unless item.finite?
356
- raise InvalidPolicyfileAttribute, "Floating point numbers cannot be infinite or NaN. You gave #{item.inspect}"
357
- end
358
- # Support for floats assumes that any implementation of our JSON
359
- # canonicalization routine will use IEEE-754 doubles. In decimal terms,
360
- # doubles give 15-17 digits of precision, so we err on the safe side
361
- # and only use 15 digits in the string conversion. We use the `g`
362
- # format, which is a documented-enough "do what I mean" where floats
363
- # >= 0.1 and < precsion are represented as floating point literals, and
364
- # other numbers use the exponent notation with a lowercase 'e'. Note
365
- # that both Ruby and Erlang document what their `g` does but have some
366
- # differences both subtle and non-subtle:
367
- #
368
- # ```ruby
369
- # format("%.15g", 0.1) #=> "0.1"
370
- # format("%.15g", 1_000_000_000.0) #=> "1000000000"
371
- # ```
372
- #
373
- # Whereas:
374
- #
375
- # ```erlang
376
- # lists:flatten(io_lib:format("~.15g", [0.1])). %=> "0.100000000000000"
377
- # lists:flatten(io_lib:format("~.15e", [1000000000.0])). %=> "1.00000000000000e+9"
378
- # ```
379
- #
380
- # Other implementations should normalize to ruby's %.15g behavior.
381
- Kernel.format("%.15g", item)
382
- when NilClass
383
- "null"
384
- when TrueClass
385
- "true"
386
- when FalseClass
387
- "false"
388
- else
389
- raise InvalidPolicyfileAttribute,
390
- "Invalid type in attributes. Only Hash, Array, String, Integer, Float, true, false, and nil are accepted. You gave #{item.inspect} (#{item.class})"
391
- end
392
- end
393
-
394
- def validate_attr_key(key)
395
- unless key.kind_of?(String)
396
- raise InvalidPolicyfileAttribute,
397
- "Attribute keys must be Strings (other types are not allowed in JSON). You gave: #{key.inspect} (#{key.class})"
398
- end
399
- end
400
-
401
- def set_name_from_lock_data(lock_data)
402
- name_attribute = lock_data["name"]
403
-
404
- raise InvalidLockfile, "lockfile does not have a `name' attribute" if name_attribute.nil?
405
-
406
- unless name_attribute.kind_of?(String)
407
- raise InvalidLockfile, "lockfile's name attribute must be a String (got: #{name_attribute.inspect})"
408
- end
409
-
410
- if name_attribute.empty?
411
- raise InvalidLockfile, "lockfile's name attribute cannot be an empty string"
412
- end
413
-
414
- @name = name_attribute
415
- end
416
-
417
- def set_run_list_from_lock_data(lock_data)
418
- run_list_attribute = lock_data["run_list"]
419
-
420
- raise InvalidLockfile, "lockfile does not have a run_list attribute" if run_list_attribute.nil?
421
-
422
- unless run_list_attribute.kind_of?(Array)
423
- raise InvalidLockfile, "lockfile's run_list must be an array of run list items (got: #{run_list_attribute.inspect})"
424
- end
425
-
426
- bad_run_list_items = run_list_attribute.select { |e| e !~ RUN_LIST_ITEM_FORMAT }
427
-
428
- unless bad_run_list_items.empty?
429
- msg = "lockfile's run_list items must be formatted like `recipe[$COOKBOOK_NAME::$RECIPE_NAME]'. Invalid items: `#{bad_run_list_items.join("' `")}'"
430
- raise InvalidLockfile, msg
431
- end
432
-
433
- @run_list = run_list_attribute
434
- end
435
-
436
- def set_named_run_lists_from_lock_data(lock_data)
437
- return unless lock_data.key?("named_run_lists")
438
-
439
- lock_data_named_run_lists = lock_data["named_run_lists"]
440
-
441
- unless lock_data_named_run_lists.kind_of?(Hash)
442
- msg = "lockfile's named_run_lists must be a Hash (JSON object). (got: #{lock_data_named_run_lists.inspect})"
443
- raise InvalidLockfile, msg
444
- end
445
-
446
- lock_data_named_run_lists.each do |name, run_list|
447
- unless name.kind_of?(String)
448
- msg = "Keys in lockfile's named_run_lists must be Strings. (got: #{name.inspect})"
449
- raise InvalidLockfile, msg
450
- end
451
- unless run_list.kind_of?(Array)
452
- msg = "Values in lockfile's named_run_lists must be Arrays. (got: #{run_list.inspect})"
453
- raise InvalidLockfile, msg
454
- end
455
- bad_run_list_items = run_list.select { |e| e !~ RUN_LIST_ITEM_FORMAT }
456
- unless bad_run_list_items.empty?
457
- msg = "lockfile's run_list items must be formatted like `recipe[$COOKBOOK_NAME::$RECIPE_NAME]'. Invalid items: `#{bad_run_list_items.join("' `")}'"
458
- raise InvalidLockfile, msg
459
- end
460
- end
461
- @named_run_lists = lock_data_named_run_lists
462
- end
463
-
464
- def set_cookbook_locks_from_lock_data(lock_data)
465
- cookbook_lock_data = lock_data["cookbook_locks"]
466
-
467
- if cookbook_lock_data.nil?
468
- raise InvalidLockfile, "lockfile does not have a cookbook_locks attribute"
469
- end
470
-
471
- unless cookbook_lock_data.kind_of?(Hash)
472
- raise InvalidLockfile, "lockfile's cookbook_locks attribute must be a Hash (JSON object). (got: #{cookbook_lock_data.inspect})"
473
- end
474
-
475
- lock_data["cookbook_locks"].each do |name, lock_info|
476
- build_cookbook_lock_from_lock_data(name, lock_info)
477
- end
478
- end
479
-
480
- def set_cookbook_locks_as_archives_from_lock_data(lock_data)
481
- cookbook_lock_data = lock_data["cookbook_locks"]
482
-
483
- if cookbook_lock_data.nil?
484
- raise InvalidLockfile, "lockfile does not have a cookbook_locks attribute"
485
- end
486
-
487
- unless cookbook_lock_data.kind_of?(Hash)
488
- raise InvalidLockfile, "lockfile's cookbook_locks attribute must be a Hash (JSON object). (got: #{cookbook_lock_data.inspect})"
489
- end
490
-
491
- lock_data["cookbook_locks"].each do |name, lock_info|
492
- build_cookbook_lock_as_archive_from_lock_data(name, lock_info)
493
- end
494
- end
495
-
496
- def set_attributes_from_lock_data(lock_data)
497
- default_attr_data = lock_data["default_attributes"]
498
-
499
- if default_attr_data.nil?
500
- raise InvalidLockfile, "lockfile does not have a `default_attributes` attribute"
501
- end
502
-
503
- unless default_attr_data.kind_of?(Hash)
504
- raise InvalidLockfile, "lockfile's `default_attributes` attribute must be a Hash (JSON object). (got: #{default_attr_data.inspect})"
505
- end
506
-
507
- override_attr_data = lock_data["override_attributes"]
508
-
509
- if override_attr_data.nil?
510
- raise InvalidLockfile, "lockfile does not have a `override_attributes` attribute"
511
- end
512
-
513
- unless override_attr_data.kind_of?(Hash)
514
- raise InvalidLockfile, "lockfile's `override_attributes` attribute must be a Hash (JSON object). (got: #{override_attr_data.inspect})"
515
- end
516
-
517
- @default_attributes = default_attr_data
518
- @override_attributes = override_attr_data
519
- end
520
-
521
- def set_solution_dependencies_from_lock_data(lock_data)
522
- soln_deps = lock_data["solution_dependencies"]
523
-
524
- if soln_deps.nil?
525
- raise InvalidLockfile, "lockfile does not have a solution_dependencies attribute"
526
- end
527
-
528
- unless soln_deps.kind_of?(Hash)
529
- raise InvalidLockfile, "lockfile's solution_dependencies attribute must be a Hash (JSON object). (got: #{soln_deps.inspect})"
530
- end
531
-
532
- s = Policyfile::SolutionDependencies.from_lock(lock_data["solution_dependencies"])
533
- @solution_dependencies = s
534
- end
535
-
536
- def set_included_policy_locks_from_lock_data(lock_data)
537
- locks = lock_data["included_policy_locks"]
538
- if locks.nil?
539
- @included_policy_locks = []
540
- else
541
- locks.each do |lock_info|
542
- if !(%w{revision_id name source_options}.all? { |key| !lock_info[key].nil? })
543
- raise InvalidLockfile, "lockfile included policy missing one of the required keys"
544
- end
545
- end
546
- @included_policy_locks = locks
547
- end
548
- end
549
-
550
- def build_cookbook_lock_from_lock_data(name, lock_info)
551
- unless lock_info.kind_of?(Hash)
552
- raise InvalidLockfile, "lockfile cookbook_locks entries must be a Hash (JSON object). (got: #{lock_info.inspect})"
553
- end
554
-
555
- if lock_info["cache_key"].nil?
556
- local_cookbook(name).build_from_lock_data(lock_info)
557
- else
558
- cached_cookbook(name).build_from_lock_data(lock_info)
559
- end
560
- end
561
-
562
- def build_cookbook_lock_as_archive_from_lock_data(name, lock_info)
563
- unless lock_info.kind_of?(Hash)
564
- raise InvalidLockfile, "lockfile cookbook_locks entries must be a Hash (JSON object). (got: #{lock_info.inspect})"
565
- end
566
-
567
- if lock_info["cache_key"].nil?
568
- local_cookbook = Policyfile::LocalCookbook.new(name, storage_config)
569
- local_cookbook.build_from_lock_data(lock_info)
570
- archived = Policyfile::ArchivedCookbook.new(local_cookbook, storage_config)
571
- @cookbook_locks[name] = archived
572
- else
573
- cached_cookbook = Policyfile::CachedCookbook.new(name, storage_config)
574
- cached_cookbook.build_from_lock_data(lock_info)
575
- archived = Policyfile::ArchivedCookbook.new(cached_cookbook, storage_config)
576
- @cookbook_locks[name] = archived
577
- end
578
- end
579
-
580
- end
581
- end
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ # Copyright:: Copyright (c) 2014-2018 Chef Software Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "digest/sha2"
20
+
21
+ require "chef-dk/policyfile/storage_config"
22
+ require "chef-dk/policyfile/cookbook_locks"
23
+ require "chef-dk/policyfile/solution_dependencies"
24
+ require "chef-dk/ui"
25
+
26
+ module ChefDK
27
+
28
+ class PolicyfileLock
29
+
30
+ class InstallReport
31
+
32
+ attr_reader :ui
33
+ attr_reader :policyfile_lock
34
+
35
+ def initialize(ui: nil, policyfile_lock: nil)
36
+ @ui = ui
37
+ @policyfile_lock = policyfile_lock
38
+
39
+ @cookbook_name_width = nil
40
+ @cookbook_version_width = nil
41
+ end
42
+
43
+ def installing_fixed_version_cookbook(cookbook_spec)
44
+ verb = cookbook_spec.installed? ? "Using " : "Installing"
45
+ ui.msg("#{verb} #{format_fixed_version_cookbook(cookbook_spec)}")
46
+ end
47
+
48
+ def installing_cookbook(cookbook_lock)
49
+ verb = cookbook_lock.installed? ? "Using " : "Installing"
50
+ ui.msg("#{verb} #{format_cookbook(cookbook_lock)}")
51
+ end
52
+
53
+ private
54
+
55
+ def format_cookbook(cookbook_lock)
56
+ "#{cookbook_lock.name.ljust(cookbook_name_width)} #{cookbook_lock.version.to_s.ljust(cookbook_version_width)}"
57
+ end
58
+
59
+ def cookbook_name_width
60
+ policyfile_lock.cookbook_locks.map { |name, _| name.size }.max
61
+ end
62
+
63
+ def cookbook_version_width
64
+ policyfile_lock.cookbook_locks.map { |_, lock| lock.version.size }.max
65
+ end
66
+ end
67
+
68
+ RUN_LIST_ITEM_FORMAT = /\Arecipe\[[^\s]+::[^\s]+\]\Z/
69
+
70
+ def self.build(storage_config)
71
+ lock = new(storage_config)
72
+ yield lock
73
+ lock
74
+ end
75
+
76
+ def self.build_from_compiler(compiler, storage_config)
77
+ lock = new(storage_config)
78
+ lock.build_from_compiler(compiler)
79
+ lock
80
+ end
81
+
82
+ include Policyfile::StorageConfigDelegation
83
+
84
+ attr_accessor :name
85
+ attr_accessor :run_list
86
+ attr_accessor :named_run_lists
87
+ attr_accessor :default_attributes
88
+ attr_accessor :override_attributes
89
+
90
+ attr_reader :solution_dependencies
91
+
92
+ attr_reader :storage_config
93
+
94
+ attr_reader :cookbook_locks
95
+
96
+ attr_reader :included_policy_locks
97
+
98
+ attr_reader :install_report
99
+
100
+ def initialize(storage_config, ui: nil)
101
+ @name = nil
102
+ @run_list = []
103
+ @named_run_lists = {}
104
+ @cookbook_locks = {}
105
+ @relative_paths_root = Dir.pwd
106
+ @storage_config = storage_config
107
+ @ui = ui || UI.null
108
+
109
+ @default_attributes = {}
110
+ @override_attributes = {}
111
+
112
+ @solution_dependencies = Policyfile::SolutionDependencies.new
113
+
114
+ @included_policy_locks = []
115
+
116
+ @install_report = InstallReport.new(ui: @ui, policyfile_lock: self)
117
+ end
118
+
119
+ def lock_data_for(cookbook_name)
120
+ @cookbook_locks[cookbook_name]
121
+ end
122
+
123
+ def cached_cookbook(name)
124
+ cached_cookbook = Policyfile::CachedCookbook.new(name, storage_config)
125
+ yield cached_cookbook if block_given?
126
+ @cookbook_locks[name] = cached_cookbook
127
+ end
128
+
129
+ def local_cookbook(name)
130
+ local_cookbook = Policyfile::LocalCookbook.new(name, storage_config)
131
+ yield local_cookbook if block_given?
132
+ @cookbook_locks[name] = local_cookbook
133
+ end
134
+
135
+ def dependencies
136
+ yield solution_dependencies
137
+ end
138
+
139
+ def to_lock
140
+ {}.tap do |lock|
141
+ lock["revision_id"] = revision_id
142
+ lock["name"] = name
143
+ lock["run_list"] = run_list
144
+ lock["named_run_lists"] = named_run_lists unless named_run_lists.empty?
145
+ lock["included_policy_locks"] = included_policy_locks
146
+ lock["cookbook_locks"] = cookbook_locks_for_lockfile
147
+ lock["default_attributes"] = default_attributes
148
+ lock["override_attributes"] = override_attributes
149
+ lock["solution_dependencies"] = solution_dependencies.to_lock
150
+ end
151
+ end
152
+
153
+ # Returns a fingerprint of the PolicyfileLock by computing the SHA1 hash of
154
+ # #canonical_revision_string
155
+ def revision_id
156
+ Digest::SHA256.new.hexdigest(canonical_revision_string)
157
+ end
158
+
159
+ # Generates a string representation of the lock data in a specialized
160
+ # format suitable for generating a checksum of the lock itself. Only data
161
+ # that modifies the behavior of a chef-client using the lockfile is
162
+ # included in this format; for example, a modification to the source
163
+ # options in a `Policyfile.rb` that yields identical code (such as
164
+ # switching to a github fork at the same revision) will not cause a change
165
+ # in the PolicyfileLock's canonical_revision_string.
166
+ #
167
+ # This format is intended to be used only for generating an identifier for
168
+ # a particular revision of a PolicyfileLock. It should not be used as a
169
+ # serialization format, and is not guaranteed to be a stable interface.
170
+ def canonical_revision_string
171
+ canonical_rev_text = ""
172
+
173
+ canonical_rev_text << "name:#{name}\n"
174
+
175
+ run_list.each do |item|
176
+ canonical_rev_text << "run-list-item:#{item}\n"
177
+ end
178
+
179
+ named_run_lists.each do |name, run_list|
180
+ run_list.each do |item|
181
+ canonical_rev_text << "named-run-list:#{name};run-list-item:#{item}\n"
182
+ end
183
+ end
184
+
185
+ cookbook_locks_for_lockfile.each do |name, lock|
186
+ canonical_rev_text << "cookbook:#{name};id:#{lock["identifier"]}\n"
187
+ end
188
+
189
+ canonical_rev_text << "default_attributes:#{canonicalize(default_attributes)}\n"
190
+
191
+ canonical_rev_text << "override_attributes:#{canonicalize(override_attributes)}\n"
192
+
193
+ canonical_rev_text
194
+ end
195
+
196
+ def cookbook_locks_for_lockfile
197
+ cookbook_locks.inject({}) do |locks_map, (name, location_spec)|
198
+ location_spec.validate!
199
+ location_spec.gather_profile_data
200
+ locks_map[name] = location_spec.to_lock
201
+ locks_map
202
+ end.sort.to_h
203
+ end
204
+
205
+ def validate_cookbooks!
206
+ cookbook_locks.each do |name, cookbook_lock|
207
+ cookbook_lock.validate!
208
+ cookbook_lock.refresh!
209
+ end
210
+
211
+ # Check that versions and dependencies are still valid. First we need to
212
+ # refresh the dependency info for everything that has changed, then we
213
+ # check that the new versions and dependencies are valid for the working
214
+ # set of cookbooks. We can't do this in a single loop because the user
215
+ # may have modified two cookbooks such that the versions and constraints
216
+ # are only valid when both changes are considered together.
217
+ cookbook_locks.each do |name, cookbook_lock|
218
+ if cookbook_lock.updated?
219
+ solution_dependencies.update_cookbook_dep(name, cookbook_lock.version, cookbook_lock.dependencies)
220
+ end
221
+ end
222
+ cookbook_locks.each do |name, cookbook_lock|
223
+ if cookbook_lock.updated?
224
+ solution_dependencies.test_conflict!(cookbook_lock.name, cookbook_lock.version)
225
+ end
226
+ end
227
+
228
+ true
229
+ end
230
+
231
+ def build_from_compiler(compiler)
232
+ @name = compiler.name
233
+
234
+ @run_list = compiler.normalized_run_list
235
+
236
+ @named_run_lists = compiler.normalized_named_run_lists
237
+
238
+ compiler.all_cookbook_location_specs.each do |cookbook_name, spec|
239
+ if spec.mirrors_canonical_upstream?
240
+ cached_cookbook(cookbook_name) do |cached_cb|
241
+ cached_cb.cache_key = spec.cache_key
242
+ cached_cb.origin = spec.uri
243
+ cached_cb.source_options = spec.source_options_for_lock
244
+ end
245
+ else
246
+ local_cookbook(cookbook_name) do |local_cb|
247
+ local_cb.source = spec.relative_path
248
+ local_cb.source_options = spec.source_options_for_lock
249
+ end
250
+ end
251
+ end
252
+
253
+ @default_attributes = compiler.default_attributes
254
+ @override_attributes = compiler.override_attributes
255
+
256
+ @solution_dependencies = compiler.solution_dependencies
257
+
258
+ @included_policy_locks = compiler.included_policies.map do |policy|
259
+ {
260
+ "name" => policy.name,
261
+ "revision_id" => policy.revision_id,
262
+ "source_options" => policy.source_options_for_lock,
263
+ }
264
+ end
265
+
266
+ self
267
+ end
268
+
269
+ def build_from_lock_data(lock_data)
270
+ set_name_from_lock_data(lock_data)
271
+ set_run_list_from_lock_data(lock_data)
272
+ set_named_run_lists_from_lock_data(lock_data)
273
+ set_cookbook_locks_from_lock_data(lock_data)
274
+ set_attributes_from_lock_data(lock_data)
275
+ set_solution_dependencies_from_lock_data(lock_data)
276
+ set_included_policy_locks_from_lock_data(lock_data)
277
+ self
278
+ end
279
+
280
+ def build_from_archive(lock_data)
281
+ set_name_from_lock_data(lock_data)
282
+ set_run_list_from_lock_data(lock_data)
283
+ set_named_run_lists_from_lock_data(lock_data)
284
+ set_cookbook_locks_as_archives_from_lock_data(lock_data)
285
+ set_attributes_from_lock_data(lock_data)
286
+ set_solution_dependencies_from_lock_data(lock_data)
287
+ set_included_policy_locks_from_lock_data(lock_data)
288
+ self
289
+ end
290
+
291
+ def install_cookbooks
292
+ # note: duplicates PolicyfileCompiler#ensure_cache_dir_exists
293
+ ensure_cache_dir_exists
294
+
295
+ cookbook_locks.each do |cookbook_name, cookbook_lock|
296
+ install_report.installing_cookbook(cookbook_lock)
297
+ cookbook_lock.install_locked
298
+ end
299
+ end
300
+
301
+ def ensure_cache_dir_exists
302
+ # note: duplicates PolicyfileCompiler#ensure_cache_dir_exists
303
+ unless File.exist?(cache_path)
304
+ FileUtils.mkdir_p(cache_path)
305
+ end
306
+ end
307
+
308
+ private
309
+
310
+ # Generates a canonical JSON representation of the attributes. Based on
311
+ # http://wiki.laptop.org/go/Canonical_JSON but not quite as strict, yet.
312
+ #
313
+ # In particular:
314
+ # - String encoding stuff isn't normalized
315
+ # - We allow floats that fit within the range/precision requirements of
316
+ # IEEE 754-2008 binary64 (double precision) numbers.
317
+ # - +/- Infinity and NaN are banned, but float/numeric size aren't checked.
318
+ # numerics should be in range [-(2**53)+1, (2**53)-1] to comply with
319
+ # IEEE 754-2008
320
+ #
321
+ # Recursive, so absurd nesting levels could cause a SystemError. Invalid
322
+ # input will cause an InvalidPolicyfileAttribute exception.
323
+ def canonicalize(attributes)
324
+ unless attributes.kind_of?(Hash)
325
+ raise "Top level attributes must be a Hash (you gave: #{attributes})"
326
+ end
327
+ canonicalize_elements(attributes)
328
+ end
329
+
330
+ def canonicalize_elements(item)
331
+ case item
332
+ when Hash
333
+ # Hash keys will sort differently based on the encoding, but after a
334
+ # JSON round trip everything will be UTF-8, so we have to normalize the
335
+ # keys to UTF-8 first so that the sort order uses the UTF-8 strings.
336
+ item_with_normalized_keys = item.inject({}) do |normalized_item, (key, value)|
337
+ validate_attr_key(key)
338
+ normalized_item[key.encode("utf-8")] = value
339
+ normalized_item
340
+ end
341
+ elements = item_with_normalized_keys.keys.sort.map do |key|
342
+ k = '"' << key << '":'
343
+ v = canonicalize_elements(item_with_normalized_keys[key])
344
+ k << v
345
+ end
346
+ "{" << elements.join(",") << "}"
347
+ when String
348
+ '"' << item.encode("utf-8") << '"'
349
+ when Array
350
+ elements = item.map { |i| canonicalize_elements(i) }
351
+ "[" << elements.join(",") << "]"
352
+ when Integer
353
+ item.to_s
354
+ when Float
355
+ unless item.finite?
356
+ raise InvalidPolicyfileAttribute, "Floating point numbers cannot be infinite or NaN. You gave #{item.inspect}"
357
+ end
358
+ # Support for floats assumes that any implementation of our JSON
359
+ # canonicalization routine will use IEEE-754 doubles. In decimal terms,
360
+ # doubles give 15-17 digits of precision, so we err on the safe side
361
+ # and only use 15 digits in the string conversion. We use the `g`
362
+ # format, which is a documented-enough "do what I mean" where floats
363
+ # >= 0.1 and < precsion are represented as floating point literals, and
364
+ # other numbers use the exponent notation with a lowercase 'e'. Note
365
+ # that both Ruby and Erlang document what their `g` does but have some
366
+ # differences both subtle and non-subtle:
367
+ #
368
+ # ```ruby
369
+ # format("%.15g", 0.1) #=> "0.1"
370
+ # format("%.15g", 1_000_000_000.0) #=> "1000000000"
371
+ # ```
372
+ #
373
+ # Whereas:
374
+ #
375
+ # ```erlang
376
+ # lists:flatten(io_lib:format("~.15g", [0.1])). %=> "0.100000000000000"
377
+ # lists:flatten(io_lib:format("~.15e", [1000000000.0])). %=> "1.00000000000000e+9"
378
+ # ```
379
+ #
380
+ # Other implementations should normalize to ruby's %.15g behavior.
381
+ Kernel.format("%.15g", item)
382
+ when NilClass
383
+ "null"
384
+ when TrueClass
385
+ "true"
386
+ when FalseClass
387
+ "false"
388
+ else
389
+ raise InvalidPolicyfileAttribute,
390
+ "Invalid type in attributes. Only Hash, Array, String, Integer, Float, true, false, and nil are accepted. You gave #{item.inspect} (#{item.class})"
391
+ end
392
+ end
393
+
394
+ def validate_attr_key(key)
395
+ unless key.kind_of?(String)
396
+ raise InvalidPolicyfileAttribute,
397
+ "Attribute keys must be Strings (other types are not allowed in JSON). You gave: #{key.inspect} (#{key.class})"
398
+ end
399
+ end
400
+
401
+ def set_name_from_lock_data(lock_data)
402
+ name_attribute = lock_data["name"]
403
+
404
+ raise InvalidLockfile, "lockfile does not have a `name' attribute" if name_attribute.nil?
405
+
406
+ unless name_attribute.kind_of?(String)
407
+ raise InvalidLockfile, "lockfile's name attribute must be a String (got: #{name_attribute.inspect})"
408
+ end
409
+
410
+ if name_attribute.empty?
411
+ raise InvalidLockfile, "lockfile's name attribute cannot be an empty string"
412
+ end
413
+
414
+ @name = name_attribute
415
+ end
416
+
417
+ def set_run_list_from_lock_data(lock_data)
418
+ run_list_attribute = lock_data["run_list"]
419
+
420
+ raise InvalidLockfile, "lockfile does not have a run_list attribute" if run_list_attribute.nil?
421
+
422
+ unless run_list_attribute.kind_of?(Array)
423
+ raise InvalidLockfile, "lockfile's run_list must be an array of run list items (got: #{run_list_attribute.inspect})"
424
+ end
425
+
426
+ bad_run_list_items = run_list_attribute.select { |e| e !~ RUN_LIST_ITEM_FORMAT }
427
+
428
+ unless bad_run_list_items.empty?
429
+ msg = "lockfile's run_list items must be formatted like `recipe[$COOKBOOK_NAME::$RECIPE_NAME]'. Invalid items: `#{bad_run_list_items.join("' `")}'"
430
+ raise InvalidLockfile, msg
431
+ end
432
+
433
+ @run_list = run_list_attribute
434
+ end
435
+
436
+ def set_named_run_lists_from_lock_data(lock_data)
437
+ return unless lock_data.key?("named_run_lists")
438
+
439
+ lock_data_named_run_lists = lock_data["named_run_lists"]
440
+
441
+ unless lock_data_named_run_lists.kind_of?(Hash)
442
+ msg = "lockfile's named_run_lists must be a Hash (JSON object). (got: #{lock_data_named_run_lists.inspect})"
443
+ raise InvalidLockfile, msg
444
+ end
445
+
446
+ lock_data_named_run_lists.each do |name, run_list|
447
+ unless name.kind_of?(String)
448
+ msg = "Keys in lockfile's named_run_lists must be Strings. (got: #{name.inspect})"
449
+ raise InvalidLockfile, msg
450
+ end
451
+ unless run_list.kind_of?(Array)
452
+ msg = "Values in lockfile's named_run_lists must be Arrays. (got: #{run_list.inspect})"
453
+ raise InvalidLockfile, msg
454
+ end
455
+ bad_run_list_items = run_list.select { |e| e !~ RUN_LIST_ITEM_FORMAT }
456
+ unless bad_run_list_items.empty?
457
+ msg = "lockfile's run_list items must be formatted like `recipe[$COOKBOOK_NAME::$RECIPE_NAME]'. Invalid items: `#{bad_run_list_items.join("' `")}'"
458
+ raise InvalidLockfile, msg
459
+ end
460
+ end
461
+ @named_run_lists = lock_data_named_run_lists
462
+ end
463
+
464
+ def set_cookbook_locks_from_lock_data(lock_data)
465
+ cookbook_lock_data = lock_data["cookbook_locks"]
466
+
467
+ if cookbook_lock_data.nil?
468
+ raise InvalidLockfile, "lockfile does not have a cookbook_locks attribute"
469
+ end
470
+
471
+ unless cookbook_lock_data.kind_of?(Hash)
472
+ raise InvalidLockfile, "lockfile's cookbook_locks attribute must be a Hash (JSON object). (got: #{cookbook_lock_data.inspect})"
473
+ end
474
+
475
+ lock_data["cookbook_locks"].each do |name, lock_info|
476
+ build_cookbook_lock_from_lock_data(name, lock_info)
477
+ end
478
+ end
479
+
480
+ def set_cookbook_locks_as_archives_from_lock_data(lock_data)
481
+ cookbook_lock_data = lock_data["cookbook_locks"]
482
+
483
+ if cookbook_lock_data.nil?
484
+ raise InvalidLockfile, "lockfile does not have a cookbook_locks attribute"
485
+ end
486
+
487
+ unless cookbook_lock_data.kind_of?(Hash)
488
+ raise InvalidLockfile, "lockfile's cookbook_locks attribute must be a Hash (JSON object). (got: #{cookbook_lock_data.inspect})"
489
+ end
490
+
491
+ lock_data["cookbook_locks"].each do |name, lock_info|
492
+ build_cookbook_lock_as_archive_from_lock_data(name, lock_info)
493
+ end
494
+ end
495
+
496
+ def set_attributes_from_lock_data(lock_data)
497
+ default_attr_data = lock_data["default_attributes"]
498
+
499
+ if default_attr_data.nil?
500
+ raise InvalidLockfile, "lockfile does not have a `default_attributes` attribute"
501
+ end
502
+
503
+ unless default_attr_data.kind_of?(Hash)
504
+ raise InvalidLockfile, "lockfile's `default_attributes` attribute must be a Hash (JSON object). (got: #{default_attr_data.inspect})"
505
+ end
506
+
507
+ override_attr_data = lock_data["override_attributes"]
508
+
509
+ if override_attr_data.nil?
510
+ raise InvalidLockfile, "lockfile does not have a `override_attributes` attribute"
511
+ end
512
+
513
+ unless override_attr_data.kind_of?(Hash)
514
+ raise InvalidLockfile, "lockfile's `override_attributes` attribute must be a Hash (JSON object). (got: #{override_attr_data.inspect})"
515
+ end
516
+
517
+ @default_attributes = default_attr_data
518
+ @override_attributes = override_attr_data
519
+ end
520
+
521
+ def set_solution_dependencies_from_lock_data(lock_data)
522
+ soln_deps = lock_data["solution_dependencies"]
523
+
524
+ if soln_deps.nil?
525
+ raise InvalidLockfile, "lockfile does not have a solution_dependencies attribute"
526
+ end
527
+
528
+ unless soln_deps.kind_of?(Hash)
529
+ raise InvalidLockfile, "lockfile's solution_dependencies attribute must be a Hash (JSON object). (got: #{soln_deps.inspect})"
530
+ end
531
+
532
+ s = Policyfile::SolutionDependencies.from_lock(lock_data["solution_dependencies"])
533
+ @solution_dependencies = s
534
+ end
535
+
536
+ def set_included_policy_locks_from_lock_data(lock_data)
537
+ locks = lock_data["included_policy_locks"]
538
+ if locks.nil?
539
+ @included_policy_locks = []
540
+ else
541
+ locks.each do |lock_info|
542
+ if !(%w{revision_id name source_options}.all? { |key| !lock_info[key].nil? })
543
+ raise InvalidLockfile, "lockfile included policy missing one of the required keys"
544
+ end
545
+ end
546
+ @included_policy_locks = locks
547
+ end
548
+ end
549
+
550
+ def build_cookbook_lock_from_lock_data(name, lock_info)
551
+ unless lock_info.kind_of?(Hash)
552
+ raise InvalidLockfile, "lockfile cookbook_locks entries must be a Hash (JSON object). (got: #{lock_info.inspect})"
553
+ end
554
+
555
+ if lock_info["cache_key"].nil?
556
+ local_cookbook(name).build_from_lock_data(lock_info)
557
+ else
558
+ cached_cookbook(name).build_from_lock_data(lock_info)
559
+ end
560
+ end
561
+
562
+ def build_cookbook_lock_as_archive_from_lock_data(name, lock_info)
563
+ unless lock_info.kind_of?(Hash)
564
+ raise InvalidLockfile, "lockfile cookbook_locks entries must be a Hash (JSON object). (got: #{lock_info.inspect})"
565
+ end
566
+
567
+ if lock_info["cache_key"].nil?
568
+ local_cookbook = Policyfile::LocalCookbook.new(name, storage_config)
569
+ local_cookbook.build_from_lock_data(lock_info)
570
+ archived = Policyfile::ArchivedCookbook.new(local_cookbook, storage_config)
571
+ @cookbook_locks[name] = archived
572
+ else
573
+ cached_cookbook = Policyfile::CachedCookbook.new(name, storage_config)
574
+ cached_cookbook.build_from_lock_data(lock_info)
575
+ archived = Policyfile::ArchivedCookbook.new(cached_cookbook, storage_config)
576
+ @cookbook_locks[name] = archived
577
+ end
578
+ end
579
+
580
+ end
581
+ end