chef-dk 0.10.0 → 0.11.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/README.md +19 -4
  4. data/Rakefile +9 -0
  5. data/chef-dk.gemspec +3 -1
  6. data/lib/chef-dk/chef_runner.rb +9 -0
  7. data/lib/chef-dk/command/export.rb +6 -0
  8. data/lib/chef-dk/command/generator_commands.rb +3 -3
  9. data/lib/chef-dk/command/generator_commands/base.rb +27 -0
  10. data/lib/chef-dk/command/update.rb +19 -0
  11. data/lib/chef-dk/configurable.rb +13 -1
  12. data/lib/chef-dk/exceptions.rb +3 -0
  13. data/lib/chef-dk/policyfile/cookbook_location_specification.rb +13 -0
  14. data/lib/chef-dk/policyfile/cookbook_locks.rb +1 -1
  15. data/lib/chef-dk/policyfile/dsl.rb +40 -2
  16. data/lib/chef-dk/policyfile_compiler.rb +43 -4
  17. data/lib/chef-dk/policyfile_services/export_repo.rb +156 -51
  18. data/lib/chef-dk/policyfile_services/install.rb +1 -0
  19. data/lib/chef-dk/policyfile_services/push_archive.rb +33 -2
  20. data/lib/chef-dk/skeletons/code_generator/files/default/chefignore +7 -5
  21. data/lib/chef-dk/skeletons/code_generator/files/default/repo/policies/README.md +1 -1
  22. data/lib/chef-dk/version.rb +1 -1
  23. data/lib/kitchen/provisioner/policyfile_zero.rb +8 -3
  24. data/spec/shared/custom_generator_cookbook.rb +15 -2
  25. data/spec/unit/chef_runner_spec.rb +28 -0
  26. data/spec/unit/command/export_spec.rb +11 -0
  27. data/spec/unit/command/generator_commands/base_spec.rb +136 -0
  28. data/spec/unit/command/update_spec.rb +24 -0
  29. data/spec/unit/configurable_spec.rb +41 -0
  30. data/spec/unit/fixtures/configurable/test_config_loader.rb +5 -0
  31. data/spec/unit/fixtures/configurable/test_configurable.rb +10 -0
  32. data/spec/unit/policyfile/cookbook_location_specification_spec.rb +21 -1
  33. data/spec/unit/policyfile/cookbook_locks_spec.rb +1 -1
  34. data/spec/unit/policyfile_demands_spec.rb +206 -0
  35. data/spec/unit/policyfile_evaluation_spec.rb +85 -0
  36. data/spec/unit/policyfile_lock_serialization_spec.rb +1 -1
  37. data/spec/unit/policyfile_services/export_repo_spec.rb +78 -36
  38. data/spec/unit/policyfile_services/install_spec.rb +20 -0
  39. data/spec/unit/policyfile_services/push_archive_spec.rb +41 -8
  40. metadata +27 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 94244db1cc86a696a390adabc245a53374d1b835
4
- data.tar.gz: 072880810b95fff7874a0db745ffbf4150718fbb
3
+ metadata.gz: c81c455d50627e20c42188704938e34cfa526f3f
4
+ data.tar.gz: e15449542b4ba8c836af7f01d88c1cc6fc6b60ff
5
5
  SHA512:
6
- metadata.gz: 9479250504d84f68b2ecffcc03828965181fc8acd4f6f126594019be414eff0cad29d4758aeac02cffbbc628e140b525fcc8b4e967fd93627fccde30807d9643
7
- data.tar.gz: ae7845cefdb1b2f6cefbd7bcda5649d30b22d0f50867ce41213d1984fc8a3d64db28d2ebdf01ead4f443a7f3c189937861fcebf296c53a4333ad63ec97122ae7
6
+ metadata.gz: cd8b9e20419e414b1ef2fb4fc3b7b8dca2e6c669f885bf1bffc36ae267a2c6265e3c7eb004fad42a11c733603621fdbb09b62b0035b4f878a31a51ee1cb288f7
7
+ data.tar.gz: 15ebcd787a65d08cf39c634c563f41e48634c5504d65d0b8d646f6c3faf40c8a3fe73a6c4d2273e288102a8012f810a42499b5c19e67b8d771ddf8fcf796f768
data/Gemfile CHANGED
@@ -19,6 +19,11 @@ source 'https://rubygems.org'
19
19
 
20
20
  gemspec :name => "chef-dk"
21
21
 
22
+ # TODO remove when Chef is released with net-ssh pinned
23
+ gem 'chef', github: 'chef/chef'
24
+ # TODO remove when chef-provisioning is released with net-ssh pinned
25
+ gem 'chef-provisioning', github: 'chef/chef-provisioning'
26
+
22
27
  group(:dev) do
23
28
  gem 'guard'
24
29
  gem 'guard-rspec'
data/README.md CHANGED
@@ -9,8 +9,8 @@ Chef Development Kit (ChefDK) brings Chef and the development tools developed by
9
9
  * [Foodcritic][]
10
10
 
11
11
  This repository contains the code for the `chef` command. The full
12
- package is built via the 'chefdk' project in
13
- [omnibus-chef](https://github.com/opscode/omnibus-chef).
12
+ package is built with omnibus. Project and component build definitions
13
+ are in the omnibus directory in this repo.
14
14
 
15
15
  ## Installation
16
16
 
@@ -240,7 +240,9 @@ echo 'eval (chef shell-init SHELL_NAME)' >> ~/.config/fish/config.fish
240
240
 
241
241
  ### Mac OS X
242
242
 
243
- You can uninstall Chef Development Kit on Mac using below commands:
243
+ You can uninstall Chef Development Kit on Mac using the below commands.
244
+
245
+ First, remove the main package files:
244
246
 
245
247
  ```sh
246
248
  # Remove the installed files
@@ -248,11 +250,24 @@ sudo rm -rf /opt/chefdk
248
250
 
249
251
  # Remove the system installation entry
250
252
  sudo pkgutil --forget com.getchef.pkg.chefdk
253
+ ```
254
+
255
+ Next, remove the symlinks which the Chef Development Kit installs. The location for these differs based on your OS X version.
251
256
 
252
- # Remove the symlinks under /usr/bin for Chef Development Kit
257
+ Pre-El Capitan:
258
+
259
+ ```sh
260
+ # Symlinks are in /usr/bin
253
261
  ls -la /usr/bin | egrep '/opt/chefdk' | awk '{ print $9 }' | sudo xargs -I % rm -f /usr/bin/%
254
262
  ```
255
263
 
264
+ Post-El Capitan:
265
+
266
+ ```sh
267
+ # Symlinks are in /usr/local/bin
268
+ ls -la /usr/local/bin | egrep '/opt/chefdk' | awk '{ print $9 }' | sudo xargs -I % rm -f /usr/local/bin/%
269
+ ```
270
+
256
271
  ### Windows
257
272
 
258
273
  You can use `Add / Remove Programs` on Windows to remove the Chef Development
data/Rakefile CHANGED
@@ -16,3 +16,12 @@
16
16
  #
17
17
 
18
18
  require "bundler/gem_tasks"
19
+
20
+ require "github_changelog_generator/task"
21
+
22
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
23
+ config.future_release = ChefDK::VERSION
24
+ config.enhancement_labels = "enhancement,Enhancement,New Feature,Feature".split(",")
25
+ config.bug_labels = "bug,Bug,Improvement,Upstream Bug".split(",")
26
+ config.exclude_labels = "duplicate,question,invalid,wontfix,no_changelog,Exclude From Changelog,Question,Discussion".split(",")
27
+ end
@@ -42,7 +42,7 @@ Gem::Specification.new do |gem|
42
42
 
43
43
  gem.add_dependency "minitar", "~> 0.5.4"
44
44
 
45
- gem.add_dependency "chef", "~> 12.0", ">= 12.2.1"
45
+ gem.add_dependency "chef", "~> 12.5"
46
46
 
47
47
  gem.add_dependency "solve", "~> 2.0", ">= 2.0.1"
48
48
 
@@ -53,6 +53,8 @@ Gem::Specification.new do |gem|
53
53
 
54
54
  gem.add_dependency "chef-provisioning", "~> 1.2"
55
55
 
56
+ gem.add_development_dependency "github_changelog_generator"
57
+
56
58
  %w(rspec-core rspec-expectations rspec-mocks).each do |dev_gem|
57
59
  gem.add_development_dependency dev_gem, "~> 3.0"
58
60
  end
@@ -17,6 +17,7 @@
17
17
 
18
18
  require 'chef-dk/exceptions'
19
19
  require 'chef-dk/service_exceptions'
20
+ require 'chef/policy_builder/dynamic'
20
21
  require 'chef'
21
22
 
22
23
  module ChefDK
@@ -71,6 +72,14 @@ module ChefDK
71
72
  Chef::Config.color = true
72
73
  Chef::Config.diff_disabled = true
73
74
 
75
+ # If the user has set policyfile configuration in the workstation config
76
+ # file, the underlying chef-client code may enable policyfile mode and
77
+ # then fail because chef-solo doesn't support policyfiles.
78
+ Chef::Config.use_policyfile = false
79
+ Chef::Config.policy_name = nil
80
+ Chef::Config.policy_group = nil
81
+ Chef::Config.deployment_group = nil
82
+
74
83
  # atomic file operations on Windows require Administrator privileges to be able to read the SACL from a file
75
84
  # Using file_staging_uses_destdir(true) will get us inherited permissions indirectly on tempfile creation
76
85
  Chef::Config.file_atomic_update = false if Chef::Platform.windows?
@@ -85,6 +85,12 @@ E
85
85
  return 1 unless apply_params!(params)
86
86
  export_service.run
87
87
  ui.msg("Exported policy '#{export_service.policyfile_lock.name}' to #{export_target}")
88
+ unless archive?
89
+ ui.msg("")
90
+ ui.msg("To converge this system with the exported policy, run:")
91
+ ui.msg(" cd #{export_dir}")
92
+ ui.msg(" chef-client -z")
93
+ end
88
94
  0
89
95
  rescue ExportDirNotEmpty => e
90
96
  ui.err("ERROR: " + e.message)
@@ -37,19 +37,19 @@ module ChefDK
37
37
  :short => "-I LICENSE",
38
38
  :long => "--license LICENSE",
39
39
  :description => "all_rights, apache2, mit, gplv2, gplv3 - defaults to all_rights",
40
- :default => "all_rights"
40
+ :default => nil
41
41
 
42
42
  option :copyright_holder,
43
43
  :short => "-C COPYRIGHT",
44
44
  :long => "--copyright COPYRIGHT",
45
45
  :description => "Name of the copyright holder - defaults to 'The Authors'",
46
- :default => "The Authors"
46
+ :default => nil
47
47
 
48
48
  option :email,
49
49
  :short => "-m EMAIL",
50
50
  :long => "--email EMAIL",
51
51
  :description => "Email address of the author - defaults to 'you@example.com'",
52
- :default => 'you@example.com'
52
+ :default => nil
53
53
 
54
54
  option :generator_cookbook,
55
55
  :short => "-g GENERATOR_COOKBOOK_PATH",
@@ -65,6 +65,7 @@ module ChefDK
65
65
 
66
66
  # Sets git related generator_context values.
67
67
  def setup_context
68
+ apply_generator_values_from_config
68
69
  Generator.add_attr_to_context(:have_git, have_git?)
69
70
  Generator.add_attr_to_context(:skip_git_init, false)
70
71
  config.each do |k,v|
@@ -115,6 +116,32 @@ module ChefDK
115
116
  config[:generator_cookbook] || chefdk_config.generator_cookbook
116
117
  end
117
118
 
119
+ # Load any values that were not defined via cli switches from Chef
120
+ # configuration
121
+ #
122
+ def apply_generator_values_from_config
123
+ config[:copyright_holder] ||= coerce_generator_copyright_holder
124
+ config[:email] ||= coerce_generator_email
125
+ config[:license] ||= coerce_generator_license
126
+ end
127
+
128
+ def coerce_generator_copyright_holder
129
+ generator_config.copyright_holder ||
130
+ knife_config.cookbook_copyright ||
131
+ "The Authors"
132
+ end
133
+
134
+ def coerce_generator_email
135
+ generator_config.email ||
136
+ knife_config.cookbook_email ||
137
+ "you@example.com"
138
+ end
139
+
140
+ def coerce_generator_license
141
+ generator_config.license ||
142
+ knife_config.cookbook_license ||
143
+ "all_rights"
144
+ end
118
145
  end
119
146
  end
120
147
  end
@@ -19,12 +19,15 @@ require 'chef-dk/command/base'
19
19
  require 'chef-dk/ui'
20
20
  require 'chef-dk/policyfile_services/install'
21
21
  require 'chef-dk/policyfile_services/update_attributes'
22
+ require 'chef-dk/configurable'
22
23
 
23
24
  module ChefDK
24
25
  module Command
25
26
 
26
27
  class Update < Base
27
28
 
29
+ include Configurable
30
+
28
31
  banner(<<-BANNER)
29
32
  Usage: chef update [ POLICY_FILE ] [options]
30
33
 
@@ -45,6 +48,11 @@ Options:
45
48
 
46
49
  BANNER
47
50
 
51
+ option :config_file,
52
+ short: "-c CONFIG_FILE",
53
+ long: "--config CONFIG_FILE",
54
+ description: "Path to configuration file"
55
+
48
56
  option :debug,
49
57
  short: "-D",
50
58
  long: "--debug",
@@ -74,6 +82,13 @@ BANNER
74
82
 
75
83
  def run(params = [])
76
84
  return 1 unless apply_params!(params)
85
+
86
+ # Force config file to be loaded. We don't use the configuration
87
+ # directly, but the user may have SSL configuration options that they
88
+ # need to talk to a private supermarket (e.g., trusted_certs or
89
+ # ssl_verify_mode)
90
+ chef_config
91
+
77
92
  if update_attributes?
78
93
  attributes_updater.run
79
94
  else
@@ -98,6 +113,10 @@ BANNER
98
113
  !!config[:debug]
99
114
  end
100
115
 
116
+ def config_path
117
+ config[:config_file]
118
+ end
119
+
101
120
  def update_attributes?
102
121
  !!config[:update_attributes]
103
122
  end
@@ -32,6 +32,12 @@ class Chef::Config
32
32
 
33
33
  default(:generator_cookbook, File.expand_path("../skeletons/code_generator", __FILE__))
34
34
 
35
+ config_context(:generator) do
36
+ config_strict_mode(true)
37
+ configurable :copyright_holder
38
+ configurable :email
39
+ configurable :license
40
+ end
35
41
  end
36
42
  end
37
43
 
@@ -52,6 +58,12 @@ module ChefDK
52
58
  @config_loader ||= Chef::WorkstationConfigLoader.new(config[:config_file])
53
59
  end
54
60
 
61
+ def generator_config
62
+ chefdk_config.generator
63
+ end
64
+
65
+ def knife_config
66
+ chef_config.knife
67
+ end
55
68
  end
56
69
  end
57
-
@@ -38,6 +38,9 @@ module ChefDK
38
38
  class CachedCookbookModified < StandardError
39
39
  end
40
40
 
41
+ class CookbookDoesNotContainRequiredRecipe < StandardError
42
+ end
43
+
41
44
  class InvalidPolicyfileAttribute < StandardError
42
45
  end
43
46
 
@@ -123,6 +123,11 @@ module ChefDK
123
123
  cached_cookbook.dependencies
124
124
  end
125
125
 
126
+ def cookbook_has_recipe?(recipe_name)
127
+ expected_path = cookbook_path.join("recipes/#{recipe_name}.rb")
128
+ expected_path.exist?
129
+ end
130
+
126
131
  def cached_cookbook
127
132
  # TODO: handle 'bad' return values here (cookbook not installed yet)
128
133
  installer.cached_cookbook
@@ -136,6 +141,14 @@ module ChefDK
136
141
  !source_options.empty? && installer.nil?
137
142
  end
138
143
 
144
+ def cookbook_path
145
+ if installer.respond_to?(:expanded_path)
146
+ installer.expanded_path
147
+ else
148
+ installer.install_path.expand_path
149
+ end
150
+ end
151
+
139
152
  end
140
153
  end
141
154
  end
@@ -446,7 +446,7 @@ module ChefDK
446
446
  # So the cookbook will be located in a path like:
447
447
  # cookbooks/nginx-111.222.333
448
448
  def cookbook_path
449
- File.join(relative_paths_root, "cookbooks", "#{name}-#{dotted_decimal_identifier}")
449
+ File.join(relative_paths_root, "cookbook_artifacts", "#{name}-#{identifier}")
450
450
  end
451
451
 
452
452
  # We trust that archived cookbooks haven't been modified, so just return
@@ -20,11 +20,14 @@ require 'chef-dk/policyfile/cookbook_location_specification'
20
20
  require 'chef-dk/policyfile/storage_config'
21
21
 
22
22
  require 'chef/node/attribute'
23
+ require 'chef/run_list/run_list_item'
23
24
 
24
25
  module ChefDK
25
26
  module Policyfile
26
27
  class DSL
27
28
 
29
+ RUN_LIST_ITEM_COMPONENT = %r/^[.[:alnum:]_-]+$/.freeze
30
+
28
31
  include StorageConfigDelegation
29
32
 
30
33
  attr_writer :name
@@ -60,12 +63,20 @@ module ChefDK
60
63
 
61
64
  def run_list(*run_list_items)
62
65
  run_list_items = run_list_items.flatten
63
- @run_list = run_list_items unless run_list_items.empty?
66
+ unless run_list_items.empty?
67
+ validate_run_list_items(run_list_items)
68
+ @run_list = run_list_items
69
+ end
64
70
  @run_list
65
71
  end
66
72
 
67
73
  def named_run_list(name, *run_list_items)
68
- @named_run_lists[name] = run_list_items.flatten
74
+ run_list_items = run_list_items.flatten
75
+ unless run_list_items.empty?
76
+ validate_run_list_items(run_list_items, name)
77
+ @named_run_lists[name] = run_list_items
78
+ end
79
+ @named_run_lists[name]
69
80
  end
70
81
 
71
82
  def default_source(source_type = nil, source_argument = nil, &block)
@@ -182,6 +193,33 @@ module ChefDK
182
193
  handle_preferred_cookbooks_conflicts
183
194
  end
184
195
 
196
+ def validate_run_list_items(items, run_list_name = nil)
197
+ items.each do |item|
198
+
199
+ run_list_desc = run_list_name.nil? ? "Run List Item '#{item}'" : "Named Run List '#{run_list_name}' Item '#{item}'"
200
+
201
+ item_name = Chef::RunList::RunListItem.new(item).name
202
+ cookbook, separator, recipe = item_name.partition('::')
203
+
204
+ if RUN_LIST_ITEM_COMPONENT.match(cookbook).nil?
205
+ message = "#{run_list_desc} has invalid cookbook name '#{cookbook}'.\nCookbook names can only contain alphanumerics, hyphens, and underscores."
206
+
207
+ # Special case when there's only one colon instead of two:
208
+ if cookbook =~ /[^:]:[^:]/
209
+ message << "\nDid you mean '#{item.sub(":", "::")}'?"
210
+ end
211
+
212
+ @errors << message
213
+ end
214
+ unless separator.empty?
215
+ # we have a cookbook and recipe
216
+ if RUN_LIST_ITEM_COMPONENT.match(recipe).nil?
217
+ @errors << "#{run_list_desc} has invalid recipe name '#{recipe}'.\nRecipe names can only contain alphanumerics, hyphens, and underscores."
218
+ end
219
+ end
220
+ end
221
+ end
222
+
185
223
  def handle_preferred_cookbooks_conflicts
186
224
  conflicting_source_messages = []
187
225
  default_source.combination(2).each do |source_a, source_b|
@@ -116,6 +116,14 @@ module ChefDK
116
116
  def install
117
117
  ensure_cache_dir_exists
118
118
 
119
+ cookbook_and_recipe_list = combined_run_lists.map(&:name).map do |recipe_spec|
120
+ cookbook, _separator, recipe = recipe_spec.partition("::")
121
+ recipe = "default" if recipe.empty?
122
+ [cookbook, recipe]
123
+ end
124
+
125
+ missing_recipes_by_cb_spec = {}
126
+
119
127
  graph_solution.each do |cookbook_name, version|
120
128
  spec = cookbook_location_spec_for(cookbook_name)
121
129
  if spec.nil? or !spec.version_fixed?
@@ -123,7 +131,29 @@ module ChefDK
123
131
  install_report.installing_cookbook(spec)
124
132
  spec.ensure_cached
125
133
  end
134
+
135
+ required_recipes = cookbook_and_recipe_list.select { |cb_name, _recipe| cb_name == spec.name }
136
+ missing_recipes = required_recipes.select {|_cb_name, recipe| !spec.cookbook_has_recipe?(recipe) }
137
+
138
+ unless missing_recipes.empty?
139
+ missing_recipes_by_cb_spec[spec] = missing_recipes
140
+ end
141
+ end
142
+
143
+ unless missing_recipes_by_cb_spec.empty?
144
+ message = "The installed cookbooks do not contain all the recipes required by your run list(s):\n"
145
+ missing_recipes_by_cb_spec.each do |spec, missing_items|
146
+ message << "#{spec.to_s}\nis missing the following required recipes:\n"
147
+ missing_items.each { |_cb, recipe| message << "* #{recipe}\n" }
148
+ end
149
+
150
+ message << "\n"
151
+ message << "You may have specified an incorrect recipe in your run list,\nor this recipe may not be available in that version of the cookbook\n"
152
+
153
+ raise CookbookDoesNotContainRequiredRecipe, message
126
154
  end
155
+
156
+
127
157
  end
128
158
 
129
159
  def create_spec_for_cookbook(cookbook_name, version)
@@ -134,7 +164,7 @@ module ChefDK
134
164
  end
135
165
 
136
166
  def all_cookbook_location_specs
137
- # in the installation proces, we create "artifact_server_cookbook_location_specs"
167
+ # in the installation process, we create "artifact_server_cookbook_location_specs"
138
168
  # for any cookbook that isn't sourced from a single-version source (e.g.,
139
169
  # path and git only support one version at a time), but we might have
140
170
  # specs for them to track additional version constraint demands. Merging
@@ -252,14 +282,23 @@ module ChefDK
252
282
  end
253
283
 
254
284
  def cookbooks_in_run_list
255
- combined_run_lists = expanded_named_run_lists.values.inject(expanded_run_list.to_a) do |accum_run_lists, run_list|
285
+ recipes = combined_run_lists.map {|recipe| recipe.name }
286
+ recipes.map { |r| r[/^([^:]+)/, 1] }
287
+ end
288
+
289
+ def combined_run_lists
290
+ expanded_named_run_lists.values.inject(expanded_run_list.to_a) do |accum_run_lists, run_list|
256
291
  accum_run_lists |= run_list.to_a
257
292
  end
293
+ end
258
294
 
259
- recipes = combined_run_lists.map {|recipe| recipe.name }
260
- recipes.map { |r| r[/^([^:]+)/, 1] }
295
+ def combined_run_lists_by_cb_name
296
+ combined_run_lists.inject({}) do |by_name_accum, run_list_item|
297
+ by_name_accum
298
+ end
261
299
  end
262
300
 
301
+
263
302
  def build
264
303
  yield @dsl
265
304
  self