chef 16.9.17-universal-mingw32 → 16.10.17-universal-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/chef-universal-mingw32.gemspec +1 -1
  4. data/chef.gemspec +10 -1
  5. data/lib/chef/compliance/fetcher/automate.rb +15 -4
  6. data/lib/chef/compliance/runner.rb +6 -8
  7. data/lib/chef/dsl/reboot_pending.rb +1 -1
  8. data/lib/chef/file_access_control/windows.rb +4 -4
  9. data/lib/chef/file_cache.rb +4 -4
  10. data/lib/chef/formatters/error_inspectors/resource_failure_inspector.rb +1 -1
  11. data/lib/chef/handler/json_file.rb +1 -1
  12. data/lib/chef/knife/bootstrap.rb +54 -4
  13. data/lib/chef/provider/mount/mount.rb +1 -1
  14. data/lib/chef/provider/package.rb +2 -2
  15. data/lib/chef/provider/package/dnf/dnf_helper.py +1 -1
  16. data/lib/chef/provider/package/freebsd/pkgng.rb +3 -1
  17. data/lib/chef/resource.rb +27 -3
  18. data/lib/chef/resource/windows_certificate.rb +47 -17
  19. data/lib/chef/resource_inspector.rb +5 -1
  20. data/lib/chef/shell.rb +32 -1
  21. data/lib/chef/util/dsc/configuration_generator.rb +1 -1
  22. data/lib/chef/version.rb +1 -1
  23. data/lib/chef/version_string.rb +1 -1
  24. data/spec/functional/resource/ohai_spec.rb +2 -10
  25. data/spec/integration/recipes/resource_action_spec.rb +14 -0
  26. data/spec/spec_helper.rb +1 -1
  27. data/spec/unit/compliance/fetcher/automate_spec.rb +8 -0
  28. data/spec/unit/compliance/runner_spec.rb +5 -7
  29. data/spec/unit/dsl/reboot_pending_spec.rb +2 -2
  30. data/spec/unit/formatters/error_inspectors/resource_failure_inspector_spec.rb +2 -2
  31. data/spec/unit/knife/bootstrap_spec.rb +42 -3
  32. data/spec/unit/knife/supermarket_share_spec.rb +5 -6
  33. data/spec/unit/provider/mount/mount_spec.rb +1 -0
  34. data/spec/unit/provider/package/dnf/python_helper_spec.rb +7 -1
  35. data/spec/unit/provider/package/freebsd/pkgng_spec.rb +1 -1
  36. data/spec/unit/resource_inspector_spec.rb +7 -2
  37. data/spec/unit/resource_spec.rb +46 -0
  38. metadata +16 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b05a4ae8661d19b5b08acd79c6a46b5fcffcad56804fa0767302ab6d0e938f85
4
- data.tar.gz: 499bbad69cddee18a6a252550d8aa1a17e6f1a7d35033cf85a30cb78f61b8930
3
+ metadata.gz: 3d0782dad85cb7b76c159476d2e0f0187270cd9d1a08916a7ec1c9ea7ab60b42
4
+ data.tar.gz: 7f5545a34ba6fda007be0b7085ef819eaf3f686f4d4e1c151bd463db1c14b7ee
5
5
  SHA512:
6
- metadata.gz: e0721739c59a5c3b539901b3c972f98161bf158f65940cbda1d32ead29340e397cb795e2b81a8be1952bae47171c597fbf8a41f31551dc7397e2ad5610704646
7
- data.tar.gz: ddad3d5be284a73469295d250659957c03123f37a7c731c417af94ec3747678d72457971c8c73097610ed0813eeec8c3c9932ab8270b3abd1bd5beba2259cf0b
6
+ metadata.gz: 2ce9dffee18c6eb75c2db3efead108314581c42b0b4c9b212b08982bf328071ad67342312e201db3cb6b569ceb5a6d9bdf76d40d9c220ed121b6bdb331da121a
7
+ data.tar.gz: 802529997941fdbe9bcfbd508a5af9f93915274bb6510945aca3067e84f961c404b12a1624d2ab7f0ead11bee745cdaecc49e6bee4ccb6112a569702e09f3e5c
data/Gemfile CHANGED
@@ -1,5 +1,8 @@
1
1
  source "https://rubygems.org"
2
2
 
3
+ # pin until issues with Windows builds in 1.14.2 are resolved
4
+ gem "ffi", "=1.13.1"
5
+
3
6
  # Note we do not use the gemspec DSL which restricts to the
4
7
  # gemspec for the current platform and filters out other platforms
5
8
  # during a bundle lock operation. We actually want dependencies from
@@ -14,7 +14,7 @@ gemspec.add_dependency "win32-service", ">= 2.1.5", "< 3.0"
14
14
  gemspec.add_dependency "wmi-lite", "~> 1.0"
15
15
  gemspec.add_dependency "win32-taskscheduler", "~> 2.0"
16
16
  gemspec.add_dependency "iso8601", ">= 0.12.1", "< 0.14" # validate 0.14 when it comes out
17
- gemspec.add_dependency "win32-certstore", "~> 0.3"
17
+ gemspec.add_dependency "win32-certstore", "~> 0.5" # 0.5+ required for specifying user vs. system store
18
18
  gemspec.extensions << "ext/win32-eventlog/Rakefile"
19
19
  gemspec.files += Dir.glob("{distro,ext}/**/*")
20
20
 
data/chef.gemspec CHANGED
@@ -1,4 +1,13 @@
1
1
  $:.unshift(File.dirname(__FILE__) + "/lib")
2
+ vs_path = File.expand_path("chef-utils/lib/chef-utils/version_string.rb", __dir__)
3
+
4
+ if File.exist?(vs_path)
5
+ # this is the moral equivalent of a require_relative since bundler makes require_relative here fail hard
6
+ eval(IO.read(vs_path))
7
+ else
8
+ # if the path doesn't exist then we're just in the wild gem and not in the git repo
9
+ require "chef-utils/version_string"
10
+ end
2
11
  require "chef/version"
3
12
 
4
13
  Gem::Specification.new do |s|
@@ -51,7 +60,7 @@ Gem::Specification.new do |s|
51
60
  s.add_dependency "iniparse", "~> 1.4"
52
61
  s.add_dependency "addressable"
53
62
  s.add_dependency "syslog-logger", "~> 1.6"
54
- s.add_dependency "uuidtools", "~> 2.1.5"
63
+ s.add_dependency "uuidtools", ">= 2.1.5", "< 3.0"
55
64
 
56
65
  s.add_dependency "proxifier", "~> 1.0"
57
66
 
@@ -32,12 +32,12 @@ class Chef
32
32
  profile_fetch_url = target[:url]
33
33
  else
34
34
  # verifies that the target e.g base/ssh exists
35
- base_path = "/compliance/profiles/#{uri.host}#{uri.path}"
36
-
35
+ profile = sanitize_profile_name(uri)
36
+ owner, id = profile.split("/")
37
37
  profile_path = if target.respond_to?(:key?) && target.key?(:version)
38
- "#{base_path}/version/#{target[:version]}/tar"
38
+ "/compliance/profiles/#{owner}/#{id}/version/#{target[:version]}/tar"
39
39
  else
40
- "#{base_path}/tar"
40
+ "/compliance/profiles/#{owner}/#{id}/tar"
41
41
  end
42
42
 
43
43
  url = URI(Chef::Config[:data_collector][:server_url])
@@ -60,6 +60,17 @@ class Chef
60
60
  nil
61
61
  end
62
62
 
63
+ # returns a parsed url for `admin/profile` or `compliance://admin/profile`
64
+ # TODO: remove in future, copied from inspec to support older versions of inspec
65
+ def self.sanitize_profile_name(profile)
66
+ uri = if URI(profile).scheme == "compliance"
67
+ URI(profile)
68
+ else
69
+ URI("compliance://#{profile}")
70
+ end
71
+ uri.to_s.sub(%r{^compliance:\/\/}, "")
72
+ end
73
+
63
74
  def to_s
64
75
  "#{ChefUtils::Dist::Automate::PRODUCT} for #{ChefUtils::Dist::Solo::PRODUCT} Fetcher"
65
76
  end
@@ -11,15 +11,17 @@ class Chef
11
11
  class Runner < EventDispatch::Base
12
12
  extend Forwardable
13
13
 
14
- attr_accessor :run_id, :recipes
14
+ attr_accessor :run_id
15
15
  attr_reader :node
16
16
  def_delegators :node, :logger
17
17
 
18
18
  def enabled?
19
- audit_cookbook_present = recipes.include?("audit::default")
19
+ # Did we parse the libraries file from the audit cookbook? This class dates back to when Chef Automate was
20
+ # renamed from Chef Visibility in 2017, so should capture all modern versions of the audit cookbook.
21
+ audit_cookbook_present = defined?(::Reporter::ChefAutomate)
20
22
 
21
- logger.info("#{self.class}##{__method__}: #{Inspec::Dist::PRODUCT_NAME} profiles? #{inspec_profiles.any?}")
22
- logger.info("#{self.class}##{__method__}: audit cookbook? #{audit_cookbook_present}")
23
+ logger.debug("#{self.class}##{__method__}: #{Inspec::Dist::PRODUCT_NAME} profiles? #{inspec_profiles.any?}")
24
+ logger.debug("#{self.class}##{__method__}: audit cookbook? #{audit_cookbook_present}")
23
25
 
24
26
  inspec_profiles.any? && !audit_cookbook_present
25
27
  end
@@ -37,10 +39,6 @@ class Chef
37
39
  self.run_id = run_status.run_id
38
40
  end
39
41
 
40
- def run_list_expanded(run_list_expansion)
41
- self.recipes = run_list_expansion.recipes
42
- end
43
-
44
42
  def run_completed(_node, _run_status)
45
43
  return unless enabled?
46
44
 
@@ -47,7 +47,7 @@ class Chef
47
47
  registry_key_exists?('HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending')
48
48
  elsif platform?("ubuntu")
49
49
  # This should work for Debian as well if update-notifier-common happens to be installed. We need an API for that.
50
- File.exists?("/var/run/reboot-required")
50
+ File.exist?("/var/run/reboot-required")
51
51
  else
52
52
  false
53
53
  end
@@ -33,7 +33,7 @@ class Chef
33
33
  module ClassMethods
34
34
  # We want to mix these in as class methods
35
35
  def writable?(path)
36
- ::File.exists?(path) && Chef::ReservedNames::Win32::File.file_access_check(
36
+ ::File.exist?(path) && Chef::ReservedNames::Win32::File.file_access_check(
37
37
  path, Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE
38
38
  )
39
39
  end
@@ -136,7 +136,7 @@ class Chef
136
136
  end
137
137
 
138
138
  def should_update_dacl?
139
- return true unless ::File.exists?(file) || ::File.symlink?(file)
139
+ return true unless ::File.exist?(file) || ::File.symlink?(file)
140
140
 
141
141
  dacl = target_dacl
142
142
  existing_dacl = existing_descriptor.dacl
@@ -170,7 +170,7 @@ class Chef
170
170
  end
171
171
 
172
172
  def should_update_group?
173
- return true unless ::File.exists?(file) || ::File.symlink?(file)
173
+ return true unless ::File.exist?(file) || ::File.symlink?(file)
174
174
 
175
175
  (group = target_group) && (group != existing_descriptor.group)
176
176
  end
@@ -190,7 +190,7 @@ class Chef
190
190
  end
191
191
 
192
192
  def should_update_owner?
193
- return true unless ::File.exists?(file) || ::File.symlink?(file)
193
+ return true unless ::File.exist?(file) || ::File.symlink?(file)
194
194
 
195
195
  (owner = target_owner) && (owner != existing_descriptor.owner)
196
196
  end
@@ -79,7 +79,7 @@ class Chef
79
79
 
80
80
  file_path_array = File.split(path)
81
81
  file_name = file_path_array.pop
82
- if File.exists?(file) && File.writable?(file)
82
+ if File.exist?(file) && File.writable?(file)
83
83
  FileUtils.mv(
84
84
  file,
85
85
  File.join(create_cache_path(File.join(file_path_array), true), file_name)
@@ -112,7 +112,7 @@ class Chef
112
112
  }
113
113
  )
114
114
  cache_path = create_cache_path(path, false)
115
- raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exists?(cache_path)
115
+ raise Chef::Exceptions::FileNotFound, "Cannot find #{cache_path} for #{path}!" unless File.exist?(cache_path)
116
116
 
117
117
  if read
118
118
  File.read(cache_path)
@@ -139,7 +139,7 @@ class Chef
139
139
  }
140
140
  )
141
141
  cache_path = create_cache_path(path, false)
142
- if File.exists?(cache_path)
142
+ if File.exist?(cache_path)
143
143
  File.unlink(cache_path)
144
144
  end
145
145
  true
@@ -186,7 +186,7 @@ class Chef
186
186
  }
187
187
  )
188
188
  full_path = create_cache_path(path, false)
189
- if File.exists?(full_path)
189
+ if File.exist?(full_path)
190
190
  true
191
191
  else
192
192
  false
@@ -66,7 +66,7 @@ class Chef
66
66
 
67
67
  @snippet ||= begin
68
68
  if (file = parse_source) && (line = parse_line(file))
69
- return nil unless ::File.exists?(file)
69
+ return nil unless ::File.exist?(file)
70
70
 
71
71
  lines = IO.readlines(file)
72
72
 
@@ -51,7 +51,7 @@ class Chef
51
51
  end
52
52
 
53
53
  def build_report_dir
54
- unless File.exists?(config[:path])
54
+ unless File.exist?(config[:path])
55
55
  FileUtils.mkdir_p(config[:path])
56
56
  File.chmod(00700, config[:path])
57
57
  end
@@ -217,6 +217,16 @@ class Chef
217
217
  description: "Execute the bootstrap via sudo with password.",
218
218
  boolean: false
219
219
 
220
+ # runtime - su user
221
+ option :su_user,
222
+ long: "--su-user NAME",
223
+ description: "The su - USER name to perform bootstrap command using a non-root user."
224
+
225
+ # runtime - su user password
226
+ option :su_password,
227
+ long: "--su-password PASSWORD",
228
+ description: "The su USER password for authentication."
229
+
220
230
  # runtime - client_builder
221
231
  option :chef_node_name,
222
232
  short: "-N NAME",
@@ -591,13 +601,31 @@ class Chef
591
601
  def perform_bootstrap(remote_bootstrap_script_path)
592
602
  ui.info("Bootstrapping #{ui.color(server_name, :bold)}")
593
603
  cmd = bootstrap_command(remote_bootstrap_script_path)
594
- r = connection.run_command(cmd) do |data|
604
+ bootstrap_run_command(cmd)
605
+ end
606
+
607
+ # Actual bootstrap command to be run on the node.
608
+ # Handles recursive calls if su USER failed to authenticate.
609
+ def bootstrap_run_command(cmd)
610
+ r = connection.run_command(cmd) do |data, channel|
595
611
  ui.msg("#{ui.color(" [#{connection.hostname}]", :cyan)} #{data}")
612
+ channel.send_data("#{config[:su_password] || config[:connection_password]}\n") if data.match?("Password:")
596
613
  end
614
+
597
615
  if r.exit_status != 0
598
616
  ui.error("The following error occurred on #{server_name}:")
599
- ui.error(r.stderr)
600
- exit 1
617
+ ui.error("#{r.stdout} #{r.stderr}".strip)
618
+ exit(r.exit_status)
619
+ end
620
+ rescue Train::UserError => e
621
+ limit ||= 0
622
+ if e.reason == :bad_su_user_password && limit < 3
623
+ limit += 1
624
+ ui.warn("Failed to authenticate su - #{config[:su_user]} to #{server_name}")
625
+ config[:su_password] = ui.ask("Enter password for su - #{config[:su_user]}@#{server_name}:", echo: false)
626
+ retry
627
+ else
628
+ raise
601
629
  end
602
630
  end
603
631
 
@@ -1082,7 +1110,17 @@ class Chef
1082
1110
  if connection.windows?
1083
1111
  "cmd.exe /C #{remote_path}"
1084
1112
  else
1085
- "sh #{remote_path}"
1113
+ cmd = "sh #{remote_path}"
1114
+
1115
+ if config[:su_user]
1116
+ # su - USER is subject to required an interactive console
1117
+ # Otherwise, it will raise: su: must be run from a terminal
1118
+ set_transport_options(pty: true)
1119
+ cmd = "su - #{config[:su_user]} -c '#{cmd}'"
1120
+ cmd = "sudo " << cmd if config[:use_sudo]
1121
+ end
1122
+
1123
+ cmd
1086
1124
  end
1087
1125
  end
1088
1126
 
@@ -1137,6 +1175,18 @@ class Chef
1137
1175
 
1138
1176
  timeout.to_i
1139
1177
  end
1178
+
1179
+ # Train::Transports::SSH::Connection#transport_options
1180
+ # Append the options to connection transport_options
1181
+ #
1182
+ # @param opts [Hash] the opts to be added to connection transport_options.
1183
+ # @return [Hash] transport_options if the opts contains any option to be set.
1184
+ #
1185
+ def set_transport_options(opts)
1186
+ return unless opts.is_a?(Hash) || !opts.empty?
1187
+
1188
+ connection&.connection&.transport_options&.merge! opts
1189
+ end
1140
1190
  end
1141
1191
  end
1142
1192
  end
@@ -203,7 +203,7 @@ class Chef
203
203
  end
204
204
  end
205
205
  # Removed "/" from the end of str, because it was causing idempotency issue.
206
- @real_device == "/" ? @real_device : @real_device.chomp("/")
206
+ (@real_device == "/" || @real_device.match?(":/$")) ? @real_device : @real_device.chomp("/")
207
207
  end
208
208
 
209
209
  def device_logstring
@@ -446,8 +446,8 @@ class Chef
446
446
  # requested new_resource.version constraints
447
447
  logger.trace("#{new_resource} has no existing installed version. Installing install #{candidate_version}")
448
448
  target_version_array.push(candidate_version)
449
- elsif version_equals?(current_version, new_version)
450
- # this is a short-circuit to avoid needing to (expensively) query the candidate_version which must come later
449
+ elsif !use_magic_version? && version_equals?(current_version, new_version)
450
+ # this is a short-circuit (mostly for the rubygems provider) to avoid needing to expensively query the candidate_version which must come later
451
451
  logger.trace("#{new_resource} #{package_name} #{new_version} is already installed")
452
452
  target_version_array.push(nil)
453
453
  elsif candidate_version.nil?
@@ -64,7 +64,7 @@ def version_tuple(versionstr):
64
64
  tmp = versionstr[colon_index + 1:dash_index]
65
65
  if tmp != '':
66
66
  v = tmp
67
- arch_index = versionstr.find('.', dash_index)
67
+ arch_index = versionstr.rfind('.', dash_index)
68
68
  if arch_index > 0:
69
69
  r = versionstr[dash_index + 1:arch_index]
70
70
  else:
@@ -42,7 +42,9 @@ class Chef
42
42
  end
43
43
 
44
44
  def current_installed_version
45
- pkg_info = shell_out!("pkg", "info", new_resource.package_name, env: nil, returns: [0, 70])
45
+ # pkgng up to version 1.15.99.7 returns 70 for pkg not found,
46
+ # later versions return 1
47
+ pkg_info = shell_out!("pkg", "info", new_resource.package_name, env: nil, returns: [0, 1, 70])
46
48
  pkg_info.stdout[/^Version +: (.+)$/, 1]
47
49
  end
48
50
 
data/lib/chef/resource.rb CHANGED
@@ -1062,6 +1062,7 @@ class Chef
1062
1062
  # action for the resource.
1063
1063
  #
1064
1064
  # @param name [Symbol] The action name to define.
1065
+ # @param description [String] optional description for the action
1065
1066
  # @param recipe_block The recipe to run when the action is taken. This block
1066
1067
  # takes no parameters, and will be evaluated in a new context containing:
1067
1068
  #
@@ -1071,14 +1072,37 @@ class Chef
1071
1072
  #
1072
1073
  # @return The Action class implementing the action
1073
1074
  #
1074
- def self.action(action, &recipe_block)
1075
+ def self.action(action, description: nil, &recipe_block)
1075
1076
  action = action.to_sym
1076
1077
  declare_action_class
1077
1078
  action_class.action(action, &recipe_block)
1078
1079
  self.allowed_actions += [ action ]
1080
+ # Accept any non-nil description, which will correctly override
1081
+ # any specific inherited description.
1082
+ action_descriptions[action] = description unless description.nil?
1079
1083
  default_action action if Array(default_action) == [:nothing]
1080
1084
  end
1081
1085
 
1086
+ # Retrieve the description for a resource's action, if
1087
+ # any description has been included in the definition.
1088
+ #
1089
+ # @param action [Symbol,String] the action name
1090
+ # @return the description of the action provided, or nil if no description
1091
+ # was defined
1092
+ def self.action_description(action)
1093
+ action_descriptions[action.to_sym]
1094
+ end
1095
+
1096
+ # @api private
1097
+ #
1098
+ # @return existing action description hash, or newly-initialized
1099
+ # hash containing action descriptions inherited from parent Resource,
1100
+ # if any.
1101
+ def self.action_descriptions
1102
+ @action_descriptions ||=
1103
+ superclass.respond_to?(:action_descriptions) ? superclass.action_descriptions.dup : { nothing: nil }
1104
+ end
1105
+
1082
1106
  # Define a method to load up this resource's properties with the current
1083
1107
  # actual values.
1084
1108
  #
@@ -1196,9 +1220,9 @@ class Chef
1196
1220
  #
1197
1221
 
1198
1222
  # FORBIDDEN_IVARS do not show up when the resource is converted to JSON (ie. hidden from data_collector and sending to the chef server via #to_json/to_h/as_json/inspect)
1199
- FORBIDDEN_IVARS = %i{@run_context @logger @not_if @only_if @enclosing_provider @description @introduced @examples @validation_message @deprecated @default_description @skip_docs @executed_by_runner}.freeze
1223
+ FORBIDDEN_IVARS = %i{@run_context @logger @not_if @only_if @enclosing_provider @description @introduced @examples @validation_message @deprecated @default_description @skip_docs @executed_by_runner @action_descriptions}.freeze
1200
1224
  # HIDDEN_IVARS do not show up when the resource is displayed to the user as text (ie. in the error inspector output via #to_text)
1201
- HIDDEN_IVARS = %i{@allowed_actions @resource_name @source_line @run_context @logger @name @not_if @only_if @elapsed_time @enclosing_provider @description @introduced @examples @validation_message @deprecated @default_description @skip_docs @executed_by_runner}.freeze
1225
+ HIDDEN_IVARS = %i{@allowed_actions @resource_name @source_line @run_context @logger @name @not_if @only_if @elapsed_time @enclosing_provider @description @introduced @examples @validation_message @deprecated @default_description @skip_docs @executed_by_runner @action_descriptions}.freeze
1202
1226
 
1203
1227
  include Chef::Mixin::ConvertToClassName
1204
1228
  extend Chef::Mixin::ConvertToClassName
@@ -76,7 +76,7 @@ class Chef
76
76
  default: "MY", equal_to: ["TRUSTEDPUBLISHER", "TrustedPublisher", "CLIENTAUTHISSUER", "REMOTE DESKTOP", "ROOT", "TRUSTEDDEVICES", "WEBHOSTING", "CA", "AUTHROOT", "TRUSTEDPEOPLE", "MY", "SMARTCARDROOT", "TRUST", "DISALLOWED"]
77
77
 
78
78
  property :user_store, [TrueClass, FalseClass],
79
- description: "Use the user store of the local machine store if set to false.",
79
+ description: "Use the `CurrentUser` store instead of the default `LocalMachine` store. Note: Prior to #{ChefUtils::Dist::Infra::CLIENT}. 16.10 this property was ignored.",
80
80
  default: false
81
81
 
82
82
  property :cert_path, String,
@@ -119,7 +119,7 @@ class Chef
119
119
  code_script << acl_script(hash)
120
120
  guard_script << cert_exists_script(hash)
121
121
 
122
- powershell_script "setting the acls on #{new_resource.source} in #{cert_location}\\#{new_resource.store_name}" do
122
+ powershell_script "setting the acls on #{new_resource.source} in #{ps_cert_location}\\#{new_resource.store_name}" do
123
123
  convert_boolean_return true
124
124
  code code_script
125
125
  only_if guard_script
@@ -161,25 +161,47 @@ class Chef
161
161
  end
162
162
 
163
163
  action_class do
164
+
165
+ CERT_SYSTEM_STORE_LOCAL_MACHINE = 0x00020000
166
+ CERT_SYSTEM_STORE_CURRENT_USER = 0x00010000
167
+
164
168
  def add_cert(cert_obj)
165
- store = ::Win32::Certstore.open(new_resource.store_name)
169
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
166
170
  store.add(cert_obj)
167
171
  end
168
172
 
169
173
  def add_pfx_cert
170
174
  exportable = new_resource.exportable ? 1 : 0
171
- store = ::Win32::Certstore.open(new_resource.store_name)
175
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
172
176
  store.add_pfx(new_resource.source, new_resource.pfx_password, exportable)
173
177
  end
174
178
 
175
179
  def delete_cert
176
- store = ::Win32::Certstore.open(new_resource.store_name)
177
- store.delete(new_resource.source)
180
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
181
+ store.delete(resolve_thumbprint(new_resource.source))
178
182
  end
179
183
 
180
184
  def fetch_cert
181
- store = ::Win32::Certstore.open(new_resource.store_name)
182
- store.get(new_resource.source)
185
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
186
+ store.get(resolve_thumbprint(new_resource.source))
187
+ end
188
+
189
+ # Thumbprints should be exactly 40 Hex characters
190
+ def valid_thumbprint?(string)
191
+ string.scan(/\H/).empty? && string.length == 40
192
+ end
193
+
194
+ def get_thumbprint(store_name, location, source)
195
+ <<-GETTHUMBPRINTCODE
196
+ $content = Get-ChildItem -Path Cert:\\#{location}\\#{store_name} | Where-Object {$_.Subject -Match "#{source}"} | Select-Object Thumbprint
197
+ $content.thumbprint
198
+ GETTHUMBPRINTCODE
199
+ end
200
+
201
+ def resolve_thumbprint(thumbprint)
202
+ return thumbprint if valid_thumbprint?(thumbprint)
203
+
204
+ powershell_exec!(get_thumbprint(new_resource.store_name, ps_cert_location, new_resource.source)).result
183
205
  end
184
206
 
185
207
  # Checks whether a certificate with the given thumbprint
@@ -187,9 +209,11 @@ class Chef
187
209
  # If the certificate is not present, verify_cert returns a String: "Certificate not found"
188
210
  # But if it is present but expired, it returns a Boolean: false
189
211
  # Otherwise, it returns a Boolean: true
212
+ # updated this method to accept either a subject name or a thumbprint - 1/29/2021
213
+
190
214
  def verify_cert(thumbprint = new_resource.source)
191
- store = ::Win32::Certstore.open(new_resource.store_name)
192
- store.valid?(thumbprint)
215
+ store = ::Win32::Certstore.open(new_resource.store_name, store_location: native_cert_location)
216
+ store.valid?(resolve_thumbprint(thumbprint))
193
217
  end
194
218
 
195
219
  def show_or_store_cert(cert_obj)
@@ -230,13 +254,19 @@ class Chef
230
254
  out_file.close
231
255
  end
232
256
 
233
- def cert_location
234
- @location ||= new_resource.user_store ? "CurrentUser" : "LocalMachine"
257
+ # this array structure is solving 2 problems. The first is that we need to have support for both the CurrentUser AND LocalMachine stores
258
+ # Secondly, we need to pass the proper constant name for each store to win32-certstore but also pass the short name to powershell scripts used here
259
+ def ps_cert_location
260
+ new_resource.user_store ? "CurrentUser" : "LocalMachine"
261
+ end
262
+
263
+ def native_cert_location
264
+ new_resource.user_store ? CERT_SYSTEM_STORE_CURRENT_USER : CERT_SYSTEM_STORE_LOCAL_MACHINE
235
265
  end
236
266
 
237
267
  def cert_script(persist)
238
268
  cert_script = "$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2"
239
- file = Chef::Util::PathHelper.cleanpath(new_resource.source)
269
+ file = Chef::Util::PathHelper.cleanpath(new_resource.source, ps_cert_location)
240
270
  cert_script << " \"#{file}\""
241
271
  if ::File.extname(file.downcase) == ".pfx"
242
272
  cert_script << ", \"#{new_resource.pfx_password}\""
@@ -252,14 +282,14 @@ class Chef
252
282
  def cert_exists_script(hash)
253
283
  <<-EOH
254
284
  $hash = #{hash}
255
- Test-Path "Cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
285
+ Test-Path "Cert:\\#{ps_cert_location}\\#{new_resource.store_name}\\$hash"
256
286
  EOH
257
287
  end
258
288
 
259
289
  def within_store_script
260
290
  inner_script = yield "$store"
261
291
  <<-EOH
262
- $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{cert_location})
292
+ $store = New-Object System.Security.Cryptography.X509Certificates.X509Store "#{new_resource.store_name}", ([System.Security.Cryptography.X509Certificates.StoreLocation]::#{ps_cert_location})
263
293
  $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
264
294
  #{inner_script}
265
295
  $store.Close()
@@ -273,7 +303,7 @@ class Chef
273
303
  # and from https://msdn.microsoft.com/en-us/library/windows/desktop/bb204778(v=vs.85).aspx
274
304
  set_acl_script = <<-EOH
275
305
  $hash = #{hash}
276
- $storeCert = Get-ChildItem "cert:\\#{cert_location}\\#{new_resource.store_name}\\$hash"
306
+ $storeCert = Get-ChildItem "cert:\\#{ps_cert_location}\\#{new_resource.store_name}\\$hash"
277
307
  if ($storeCert -eq $null) { throw 'no key exists.' }
278
308
  $keyname = $storeCert.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
279
309
  if ($keyname -eq $null) { throw 'no private key exists.' }
@@ -340,7 +370,7 @@ class Chef
340
370
  if verify_cert(thumbprint) == true
341
371
  Chef::Log.debug("Certificate is already present")
342
372
  else
343
- converge_by("Adding certificate #{new_resource.source} into Store #{new_resource.store_name}") do
373
+ converge_by("Adding certificate #{new_resource.source} into #{ps_cert_location} Store #{new_resource.store_name}") do
344
374
  if is_pfx
345
375
  add_pfx_cert
346
376
  else
@@ -41,7 +41,11 @@ class Chef
41
41
  data[:description] = resource.description
42
42
  # data[:deprecated] = resource.deprecated || false
43
43
  data[:default_action] = resource.default_action
44
- data[:actions] = resource.allowed_actions
44
+ data[:actions] = {}
45
+ resource.allowed_actions.each do |action|
46
+ data[:actions][action] = resource.action_description(action)
47
+ end
48
+
45
49
  data[:examples] = resource.examples
46
50
  data[:introduced] = resource.introduced
47
51
  data[:preview] = resource.preview_resource
data/lib/chef/shell.rb CHANGED
@@ -25,6 +25,7 @@ require "pp" unless defined?(PP)
25
25
  require "etc" unless defined?(Etc)
26
26
  require "mixlib/cli" unless defined?(Mixlib::CLI)
27
27
  require "chef-utils/dist" unless defined?(ChefUtils::Dist)
28
+ require "chef-config/mixin/dot_d"
28
29
 
29
30
  require_relative "../chef"
30
31
  require_relative "version"
@@ -211,6 +212,7 @@ module Shell
211
212
 
212
213
  class Options
213
214
  include Mixlib::CLI
215
+ include ChefConfig::Mixin::DotD
214
216
 
215
217
  def self.footer(text = nil)
216
218
  @footer = text if text
@@ -341,15 +343,44 @@ module Shell
341
343
  # We have to nuke ARGV to make sure irb's option parser never sees it.
342
344
  # otherwise, IRB complains about command line switches it doesn't recognize.
343
345
  ARGV.clear
346
+
347
+ # This code should not exist.
348
+ # We should be using Application::Client and then calling load_config_file
349
+ # which does all this properly. However this will do for now.
344
350
  config[:config_file] = config_file_for_shell_mode(environment)
345
351
  config_msg = config[:config_file] || "none (standalone session)"
346
352
  puts "loading configuration: #{config_msg}"
347
- Chef::Config.from_file(config[:config_file]) if !config[:config_file].nil? && File.exist?(config[:config_file]) && File.readable?(config[:config_file])
353
+
354
+ # load the config (if we have one)
355
+ unless config[:config_file].nil?
356
+ if File.exist?(config[:config_file]) && File.readable?(config[:config_file])
357
+ Chef::Config.from_file(config[:config_file])
358
+ end
359
+
360
+ # even if we couldn't load that, we need to tell Chef::Config what
361
+ # the file was so it sets conf dir and d_dir and such properly
362
+ Chef::Config[:config_file] = config[:config_file]
363
+
364
+ # now attempt to load any relevant dot-dirs
365
+ load_dot_d(Chef::Config[:client_d_dir]) if Chef::Config[:client_d_dir]
366
+ end
367
+
368
+ # finally merge command-line options in
348
369
  Chef::Config.merge!(config)
349
370
  end
350
371
 
351
372
  private
352
373
 
374
+ # shamelessly lifted from application.rb
375
+ def apply_config(config_content, config_file_path)
376
+ Chef::Config.from_string(config_content, config_file_path)
377
+ rescue Exception => error
378
+ logger.fatal("Configuration error #{error.class}: #{error.message}")
379
+ filtered_trace = error.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
380
+ filtered_trace.each { |line| logger.fatal(" " + line ) }
381
+ raise Chef::Exceptions::ConfigurationError.new("Aborting due to error in '#{config_file_path}': #{error}")
382
+ end
383
+
353
384
  def config_file_for_shell_mode(environment)
354
385
  dot_chef_dir = Chef::Util::PathHelper.home(".chef")
355
386
  if config[:config_file]
@@ -105,7 +105,7 @@ class Chef::Util::DSC
105
105
  # The name may not be null or empty, and should start with a letter.
106
106
  def validate_configuration_name!(configuration_name)
107
107
  if !!(configuration_name =~ /\A[A-Za-z]+[_a-zA-Z0-9]*\Z/) == false
108
- raise ArgumentError, 'Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name'
108
+ raise ArgumentError, "Configuration `#{configuration_name}` is not a valid PowerShell cmdlet name"
109
109
  end
110
110
  end
111
111
 
data/lib/chef/version.rb CHANGED
@@ -23,7 +23,7 @@ require_relative "version_string"
23
23
 
24
24
  class Chef
25
25
  CHEF_ROOT = File.expand_path("..", __dir__)
26
- VERSION = Chef::VersionString.new("16.9.17")
26
+ VERSION = Chef::VersionString.new("16.10.17")
27
27
  end
28
28
 
29
29
  #
@@ -13,7 +13,7 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
- require "chef-utils/version_string"
16
+ require "chef-utils/version_string" unless defined?(ChefUtils::VersionString)
17
17
 
18
18
  class Chef
19
19
  VersionString = ChefUtils::VersionString
@@ -19,10 +19,6 @@
19
19
  require "spec_helper"
20
20
 
21
21
  describe Chef::Resource::Ohai do
22
- let(:ohai) do
23
- OHAI_SYSTEM
24
- end
25
-
26
22
  let(:node) { Chef::Node.new }
27
23
 
28
24
  let(:run_context) do
@@ -34,13 +30,9 @@ describe Chef::Resource::Ohai do
34
30
 
35
31
  shared_examples_for "reloaded :uptime" do
36
32
  it "should reload :uptime" do
37
- initial_uptime = ohai[:uptime]
38
-
39
- # Sleep for a second so the uptime gets updated.
40
- sleep 1
41
-
33
+ expect(node[:uptime_seconds]).to be nil
42
34
  ohai_resource.run_action(:reload)
43
- expect(node[:uptime]).not_to eq(initial_uptime)
35
+ expect(Integer(node[:uptime_seconds])).to be_an(Integer)
44
36
  end
45
37
  end
46
38
 
@@ -223,6 +223,10 @@ module ResourceActionSpec
223
223
  ActionJackson.succeeded = ActionJackson.ruby_block_converged
224
224
  end
225
225
 
226
+ action :test1, description: "Original description" do
227
+ true
228
+ end
229
+
226
230
  def foo_public
227
231
  "foo_public!"
228
232
  end
@@ -293,7 +297,12 @@ module ResourceActionSpec
293
297
  ActionJackalope.jackalope_ran = :access_attribute
294
298
  ActionJackalope.succeeded = ActionJackson.succeeded
295
299
  end
300
+
301
+ action :test1, description: "An old action with a new description" do
302
+ super
303
+ end
296
304
  end
305
+
297
306
  before do
298
307
  ActionJackalope.jackalope_ran = nil
299
308
  ActionJackalope.load_current_resource_ran = nil
@@ -344,6 +353,11 @@ module ResourceActionSpec
344
353
  expect(ActionJackalope.succeeded).to eq "foo!alope blarghle! bar!alope"
345
354
  end
346
355
 
356
+ it "allows overridden action to have a description separate from the action defined in the base resource" do
357
+ expect(ActionJackson.action_description(:test1)).to eql "Original description"
358
+ expect(ActionJackalope.action_description(:test1)).to eql "An old action with a new description"
359
+ end
360
+
347
361
  it "non-overridden actions run and can access overridden and non-overridden variables (but not necessarily new ones)" do
348
362
  converge do
349
363
  action_jackalope "hi" do
data/spec/spec_helper.rb CHANGED
@@ -87,7 +87,7 @@ Dir["spec/support/**/*.rb"]
87
87
  .each { |f| require f }
88
88
 
89
89
  OHAI_SYSTEM = Ohai::System.new
90
- OHAI_SYSTEM.all_plugins(["platform", "hostname", "languages/powershell"])
90
+ OHAI_SYSTEM.all_plugins(["platform", "hostname", "languages/powershell", "uptime"])
91
91
 
92
92
  test_node = Chef::Node.new
93
93
  test_node.automatic["os"] = (OHAI_SYSTEM["os"] || "unknown_os").dup.freeze
@@ -21,6 +21,14 @@ describe Chef::Compliance::Fetcher::Automate do
21
21
  expect(res.target).to eq(expected)
22
22
  end
23
23
 
24
+ it "should resolve a compliance URL with a @ in the namespace" do
25
+ res = Chef::Compliance::Fetcher::Automate.resolve("compliance://name@space/profile_name")
26
+
27
+ expect(res).to be_kind_of(Chef::Compliance::Fetcher::Automate)
28
+ expected = "https://automate.test/compliance/profiles/name@space/profile_name/tar"
29
+ expect(res.target).to eq(expected)
30
+ end
31
+
24
32
  it "raises an exception with no data collector token" do
25
33
  Chef::Config[:data_collector].delete(:token)
26
34
 
@@ -8,41 +8,39 @@ describe Chef::Compliance::Runner do
8
8
  described_class.new.tap do |r|
9
9
  r.node = node
10
10
  r.run_id = "my_run_id"
11
- r.recipes = []
12
11
  end
13
12
  end
14
13
 
15
14
  describe "#enabled?" do
15
+
16
16
  it "is true if the node attributes have audit profiles and the audit cookbook is not present" do
17
17
  node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
18
- runner.recipes = %w{ fancy_cookbook::fanciness tacobell::nachos }
19
18
 
20
19
  expect(runner).to be_enabled
21
20
  end
22
21
 
23
22
  it "is false if the node attributes have audit profiles and the audit cookbook is present" do
23
+ stub_const("::Reporter::ChefAutomate", true)
24
24
  node.normal["audit"]["profiles"]["ssh"] = { 'compliance': "base/ssh" }
25
- runner.recipes = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
26
25
 
27
26
  expect(runner).not_to be_enabled
28
27
  end
29
28
 
30
29
  it "is false if the node attributes do not have audit profiles and the audit cookbook is not present" do
31
30
  node.normal["audit"]["profiles"] = {}
32
- runner.recipes = %w{ fancy_cookbook::fanciness tacobell::nachos }
33
31
 
34
32
  expect(runner).not_to be_enabled
35
33
  end
36
34
 
37
35
  it "is false if the node attributes do not have audit profiles and the audit cookbook is present" do
38
- node.normal["audit"]["profiles"] = {}
39
- runner.recipes = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
36
+ stub_const("::Reporter::ChefAutomate", true)
37
+ node.automatic["recipes"] = %w{ audit::default fancy_cookbook::fanciness tacobell::nachos }
40
38
 
41
39
  expect(runner).not_to be_enabled
42
40
  end
43
41
 
44
42
  it "is false if the node attributes do not have audit attributes and the audit cookbook is not present" do
45
- runner.recipes = %w{ fancy_cookbook::fanciness tacobell::nachos }
43
+ node.automatic["recipes"] = %w{ fancy_cookbook::fanciness tacobell::nachos }
46
44
  expect(runner).not_to be_enabled
47
45
  end
48
46
  end
@@ -57,12 +57,12 @@ describe Chef::DSL::RebootPending do
57
57
  end
58
58
 
59
59
  it "should return true if /var/run/reboot-required exists" do
60
- allow(File).to receive(:exists?).with("/var/run/reboot-required").and_return(true)
60
+ allow(File).to receive(:exist?).with("/var/run/reboot-required").and_return(true)
61
61
  expect(recipe.reboot_pending?).to be_truthy
62
62
  end
63
63
 
64
64
  it "should return false if /var/run/reboot-required does not exist" do
65
- allow(File).to receive(:exists?).with("/var/run/reboot-required").and_return(false)
65
+ allow(File).to receive(:exist?).with("/var/run/reboot-required").and_return(false)
66
66
  expect(recipe.reboot_pending?).to be_falsey
67
67
  end
68
68
  end
@@ -115,7 +115,7 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
115
115
  # fake code to run through #recipe_snippet
116
116
  source_file = [ "if true", "var = non_existent", "end" ]
117
117
  allow(IO).to receive(:readlines).and_return(source_file)
118
- allow(File).to receive(:exists?).and_return(true)
118
+ allow(File).to receive(:exist?).and_return(true)
119
119
  end
120
120
 
121
121
  it "parses a Windows path" do
@@ -141,7 +141,7 @@ describe Chef::Formatters::ErrorInspectors::ResourceFailureInspector do
141
141
 
142
142
  context "when the recipe file does not exist" do
143
143
  before do
144
- allow(File).to receive(:exists?).and_return(false)
144
+ allow(File).to receive(:exist?).and_return(false)
145
145
  allow(IO).to receive(:readlines).and_raise(Errno::ENOENT)
146
146
  end
147
147
 
@@ -1726,7 +1726,8 @@ describe Chef::Knife::Bootstrap do
1726
1726
 
1727
1727
  describe "#perform_bootstrap" do
1728
1728
  let(:exit_status) { 0 }
1729
- let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message") }
1729
+ let(:stdout) { "" }
1730
+ let(:result_mock) { double("result", exit_status: exit_status, stderr: "A message", stdout: stdout) }
1730
1731
 
1731
1732
  before do
1732
1733
  allow(connection).to receive(:hostname).and_return "testhost"
@@ -1739,12 +1740,13 @@ describe Chef::Knife::Bootstrap do
1739
1740
  expect(connection)
1740
1741
  .to receive(:run_command)
1741
1742
  .with("sh /path.sh")
1742
- .and_yield("output here")
1743
+ .and_yield("output here", nil)
1743
1744
  .and_return result_mock
1744
1745
 
1745
1746
  expect(knife.ui).to receive(:msg).with(/testhost/)
1746
1747
  knife.perform_bootstrap("/path.sh")
1747
1748
  end
1749
+
1748
1750
  context "when the remote command fails" do
1749
1751
  let(:exit_status) { 1 }
1750
1752
  it "shows an error and exits" do
@@ -1756,6 +1758,25 @@ describe Chef::Knife::Bootstrap do
1756
1758
  expect { knife.perform_bootstrap("/path.sh") }.to raise_error(SystemExit)
1757
1759
  end
1758
1760
  end
1761
+
1762
+ context "when the remote command failed due to su auth error" do
1763
+ let(:exit_status) { 1 }
1764
+ let(:stdout) { "su: Authentication failure" }
1765
+ let(:connection_obj) { double("connection", transport_options: {}) }
1766
+ it "shows an error and exits" do
1767
+ allow(connection).to receive(:connection).and_return(connection_obj)
1768
+ expect(knife.ui).to receive(:info).with(/Bootstrapping.*/)
1769
+ expect(knife).to receive(:bootstrap_command)
1770
+ .with("/path.sh")
1771
+ .and_return("su - USER -c 'sh /path.sh'")
1772
+ expect(connection)
1773
+ .to receive(:run_command)
1774
+ .with("su - USER -c 'sh /path.sh'")
1775
+ .and_yield("output here", nil)
1776
+ .and_raise(Train::UserError)
1777
+ expect { knife.perform_bootstrap("/path.sh") }.to raise_error(Train::UserError)
1778
+ end
1779
+ end
1759
1780
  end
1760
1781
 
1761
1782
  describe "#connect!" do
@@ -1964,7 +1985,25 @@ describe Chef::Knife::Bootstrap do
1964
1985
  context "under Linux" do
1965
1986
  let(:linux_test) { true }
1966
1987
  it "prefixes the command to run under sh" do
1967
- expect(knife.bootstrap_command("bootstrap")).to eq "sh bootstrap"
1988
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "sh bootstrap.sh"
1989
+ end
1990
+
1991
+ context "with --su-user option" do
1992
+ let(:connection_obj) { double("connection", transport_options: {}) }
1993
+ before do
1994
+ knife.config[:su_user] = "root"
1995
+ allow(connection).to receive(:connection).and_return(connection_obj)
1996
+ end
1997
+ it "prefixes the command to run using su -USER -c" do
1998
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
1999
+ expect(connection_obj.transport_options.key?(:pty)).to eq true
2000
+ end
2001
+
2002
+ it "sudo appended if --sudo option enabled" do
2003
+ knife.config[:use_sudo] = true
2004
+ expect(knife.bootstrap_command("bootstrap.sh")).to eq "sudo su - #{knife.config[:su_user]} -c 'sh bootstrap.sh'"
2005
+ expect(connection_obj.transport_options.key?(:pty)).to eq true
2006
+ end
1968
2007
  end
1969
2008
  end
1970
2009
  end
@@ -46,7 +46,9 @@ describe Chef::Knife::SupermarketShare do
46
46
 
47
47
  allow(@knife).to receive(:shell_out!).and_return(true)
48
48
  @stdout = StringIO.new
49
+ @stderr = StringIO.new
49
50
  allow(@knife.ui).to receive(:stdout).and_return(@stdout)
51
+ allow(@knife.ui).to receive(:stderr).and_return(@stderr)
50
52
  end
51
53
 
52
54
  describe "run" do
@@ -140,7 +142,9 @@ describe Chef::Knife::SupermarketShare do
140
142
  before do
141
143
  allow(Chef::CookbookSiteStreamingUploader).to receive(:create_build_dir).and_return("/var/tmp/dummy")
142
144
  @knife.config = { dry_run: true }
143
- allow(@knife).to receive_message_chain(:shell_out!, :stdout).and_return("file")
145
+ @so = instance_double("Mixlib::ShellOut")
146
+ allow(@knife).to receive(:shell_out!).and_return(@so)
147
+ allow(@so).to receive(:stdout).and_return("file")
144
148
  end
145
149
 
146
150
  it "should list files in the tarball" do
@@ -151,7 +155,6 @@ describe Chef::Knife::SupermarketShare do
151
155
  end
152
156
 
153
157
  it "does not upload the cookbook" do
154
- allow(@knife).to receive(:shell_out!).and_return(true)
155
158
  expect(@knife).not_to receive(:do_upload)
156
159
  @knife.run
157
160
  end
@@ -164,10 +167,6 @@ describe Chef::Knife::SupermarketShare do
164
167
  @upload_response = double("Net::HTTPResponse")
165
168
  allow(Chef::CookbookSiteStreamingUploader).to receive(:post).and_return(@upload_response)
166
169
 
167
- @stdout = StringIO.new
168
- @stderr = StringIO.new
169
- allow(@knife.ui).to receive(:stdout).and_return(@stdout)
170
- allow(@knife.ui).to receive(:stderr).and_return(@stderr)
171
170
  allow(File).to receive(:open).and_return(true)
172
171
  end
173
172
 
@@ -66,6 +66,7 @@ describe Chef::Provider::Mount::Mount do
66
66
 
67
67
  describe "when dealing with network mounts" do
68
68
  { "nfs" => "nfsserver:/vol/path",
69
+ "cephfs" => "cephserver:6789:/",
69
70
  "cifs" => "//cifsserver/share" }.each do |type, fs_spec|
70
71
  it "should detect network fs_spec (#{type})" do
71
72
  @new_resource.device fs_spec
@@ -19,11 +19,17 @@ require "spec_helper"
19
19
 
20
20
  # NOTE: most of the tests of this functionality are baked into the func tests for the dnf package provider
21
21
 
22
- describe Chef::Provider::Package::Dnf::PythonHelper do
22
+ # run this test only for following platforms.
23
+ exclude_test = !(%w{rhel fedora amazon}.include?(ohai[:platform_family]) && File.exist?("/usr/bin/dnf"))
24
+ describe Chef::Provider::Package::Dnf::PythonHelper, :requires_root, external: exclude_test do
23
25
  let(:helper) { Chef::Provider::Package::Dnf::PythonHelper.instance }
24
26
 
25
27
  it "propagates stacktraces on stderr from the forked subprocess", :rhel do
26
28
  allow(helper).to receive(:dnf_command).and_return("ruby -e 'raise \"your hands in the air\"'")
27
29
  expect { helper.package_query(:whatprovides, "tcpdump") }.to raise_error(/your hands in the air/)
28
30
  end
31
+
32
+ it "compares EVRAs with dots in the release correctly" do
33
+ expect(helper.compare_versions("0:1.8.29-6.el8.x86_64", "0:1.8.29-6.el8_3.1.x86_64")).to eql(-1)
34
+ end
29
35
  end
@@ -66,7 +66,7 @@ describe Chef::Provider::Package::Freebsd::Port do
66
66
  end
67
67
 
68
68
  it "should query pkg database" do
69
- expect(@provider).to receive(:shell_out_compacted!).with("pkg", "info", "zsh", env: nil, returns: [0, 70], timeout: 900).and_return(@pkg_info)
69
+ expect(@provider).to receive(:shell_out_compacted!).with("pkg", "info", "zsh", env: nil, returns: [0, 1, 70], timeout: 900).and_return(@pkg_info)
70
70
  expect(@provider.current_installed_version).to eq("3.1.7")
71
71
  end
72
72
  end
@@ -28,7 +28,11 @@ class DummyResource < Chef::Resource
28
28
  introduced "14.0"
29
29
  property :first, String, description: "My First Property", introduced: "14.0"
30
30
 
31
- action :dummy do
31
+ action :dummy, description: "Dummy action" do
32
+ return true
33
+ end
34
+
35
+ action :dummy_no_desc do
32
36
  return true
33
37
  end
34
38
  end
@@ -39,7 +43,8 @@ describe Chef::ResourceInspector do
39
43
 
40
44
  it "returns a hash with required data" do
41
45
  expect(subject[:description]).to eq "A dummy resource"
42
- expect(subject[:actions]).to match_array %i{nothing dummy}
46
+ expect(subject[:actions]).to eq({ nothing: nil, dummy: "Dummy action",
47
+ dummy_no_desc: nil })
43
48
  end
44
49
 
45
50
  context "excluding built in properties" do
@@ -1162,6 +1162,52 @@ describe Chef::Resource do
1162
1162
  end
1163
1163
  end
1164
1164
 
1165
+ describe "#action_description" do
1166
+ class TestResource < ::Chef::Resource
1167
+ action :symbol_action, description: "a symbol test" do; end
1168
+ action "string_action", description: "a string test" do; end
1169
+ action :base_action0 do; end
1170
+ action :base_action1, description: "unmodified base action 1 desc" do; end
1171
+ action :base_action2, description: "unmodified base action 2 desc" do; end
1172
+ action :base_action3, description: "unmodified base action 3 desc" do; end
1173
+ end
1174
+
1175
+ it "returns nil when no description was provided for the action" do
1176
+ expect(TestResource.action_description(:base_action0)).to eql(nil)
1177
+ end
1178
+
1179
+ context "when action definition is a string" do
1180
+ it "returns the description whether a symbol or string is used to look it up" do
1181
+ expect(TestResource.action_description("string_action")).to eql("a string test")
1182
+ expect(TestResource.action_description(:string_action)).to eql("a string test")
1183
+ end
1184
+ end
1185
+
1186
+ context "when action definition is a symbol" do
1187
+ it "returns the description whether a symbol or string is used to look up" do
1188
+ expect(TestResource.action_description("symbol_action")).to eql("a symbol test")
1189
+ expect(TestResource.action_description(:symbol_action)).to eql("a symbol test")
1190
+ end
1191
+ end
1192
+
1193
+ context "when inheriting from an existing resource" do
1194
+ class TestResourceChild < TestResource
1195
+ action :base_action2, description: "modified base action 2 desc" do; end
1196
+ action :base_action3 do; end
1197
+ end
1198
+
1199
+ it "returns original description when a described action is not overridden in child resource" do
1200
+ expect(TestResourceChild.action_description(:base_action1)).to eq "unmodified base action 1 desc"
1201
+ end
1202
+ it "returns original description when the child resource overrides an inherited action but NOT its description" do
1203
+ expect(TestResourceChild.action_description(:base_action3)).to eq "unmodified base action 3 desc"
1204
+ end
1205
+ it "returns new description when the child resource overrides an inherited action and its description" do
1206
+ expect(TestResourceChild.action_description(:base_action2)).to eq "modified base action 2 desc"
1207
+ end
1208
+ end
1209
+ end
1210
+
1165
1211
  describe ".default_action" do
1166
1212
  let(:default_action) {}
1167
1213
  let(:resource_class) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef
3
3
  version: !ruby/object:Gem::Version
4
- version: 16.9.17
4
+ version: 16.10.17
5
5
  platform: universal-mingw32
6
6
  authors:
7
7
  - Adam Jacob
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-08 00:00:00.000000000 Z
11
+ date: 2021-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: chef-config
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 16.9.17
19
+ version: 16.10.17
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 16.9.17
26
+ version: 16.10.17
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: chef-utils
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 16.9.17
33
+ version: 16.10.17
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 16.9.17
40
+ version: 16.10.17
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: train-core
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -554,16 +554,22 @@ dependencies:
554
554
  name: uuidtools
555
555
  requirement: !ruby/object:Gem::Requirement
556
556
  requirements:
557
- - - "~>"
557
+ - - ">="
558
558
  - !ruby/object:Gem::Version
559
559
  version: 2.1.5
560
+ - - "<"
561
+ - !ruby/object:Gem::Version
562
+ version: '3.0'
560
563
  type: :runtime
561
564
  prerelease: false
562
565
  version_requirements: !ruby/object:Gem::Requirement
563
566
  requirements:
564
- - - "~>"
567
+ - - ">="
565
568
  - !ruby/object:Gem::Version
566
569
  version: 2.1.5
570
+ - - "<"
571
+ - !ruby/object:Gem::Version
572
+ version: '3.0'
567
573
  - !ruby/object:Gem::Dependency
568
574
  name: proxifier
569
575
  requirement: !ruby/object:Gem::Requirement
@@ -750,14 +756,14 @@ dependencies:
750
756
  requirements:
751
757
  - - "~>"
752
758
  - !ruby/object:Gem::Version
753
- version: '0.3'
759
+ version: '0.5'
754
760
  type: :runtime
755
761
  prerelease: false
756
762
  version_requirements: !ruby/object:Gem::Requirement
757
763
  requirements:
758
764
  - - "~>"
759
765
  - !ruby/object:Gem::Version
760
- version: '0.3'
766
+ version: '0.5'
761
767
  description: A systems integration framework, built to bring the benefits of configuration
762
768
  management to your entire infrastructure.
763
769
  email: adam@chef.io