chef-dk 0.10.0 → 0.11.0

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