chef 19.2.12 → 19.3.14

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 +11 -16
  3. data/README.md +6 -1
  4. data/Rakefile +1 -0
  5. data/chef-universal-mingw-ucrt.gemspec +9 -2
  6. data/chef.gemspec +18 -8
  7. data/lib/chef/application/client.rb +7 -1
  8. data/lib/chef/chef_fs/file_system/chef_server/cookbook_dir.rb +1 -1
  9. data/lib/chef/client.rb +22 -4
  10. data/lib/chef/compliance/runner.rb +19 -1
  11. data/lib/chef/cookbook/gem_installer.rb +1 -1
  12. data/lib/chef/cookbook_uploader.rb +1 -1
  13. data/lib/chef/dsl/rest_resource.rb +63 -12
  14. data/lib/chef/file_access_control/windows.rb +6 -0
  15. data/lib/chef/file_access_control.rb +12 -1
  16. data/lib/chef/handler/slow_report.rb +3 -4
  17. data/lib/chef/licensing.rb +26 -6
  18. data/lib/chef/node.rb +13 -1
  19. data/lib/chef/policy_builder/expand_node_object.rb +12 -1
  20. data/lib/chef/policy_builder/policyfile.rb +12 -0
  21. data/lib/chef/property.rb +1 -1
  22. data/lib/chef/provider/file/content.rb +3 -2
  23. data/lib/chef/provider/file.rb +5 -2
  24. data/lib/chef/provider/ifconfig/redhat.rb +1 -1
  25. data/lib/chef/provider/package/dnf/dnf_helper.py +355 -65
  26. data/lib/chef/provider/package/dnf/python_helper.rb +6 -3
  27. data/lib/chef/provider/package/dnf.rb +24 -5
  28. data/lib/chef/provider/package/yum.rb +1 -1
  29. data/lib/chef/provider/package/yum_tm.rb +1 -1
  30. data/lib/chef/resource/_rest_resource.rb +4 -2
  31. data/lib/chef/resource/build_essential.rb +10 -1
  32. data/lib/chef/resource/execute.rb +0 -15
  33. data/lib/chef/resource/yum_package.rb +1 -1
  34. data/lib/chef/target_io/support.rb +1 -1
  35. data/lib/chef/target_io/train/dir.rb +1 -1
  36. data/lib/chef/target_io/train/file.rb +5 -5
  37. data/lib/chef/target_io/train/fileutils.rb +1 -1
  38. data/lib/chef/version.rb +1 -1
  39. data/lib/chef/win32/version.rb +17 -16
  40. metadata +37 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a348124039267c43844767273ea5e8727b35d92b641fc3e6690885947277b543
4
- data.tar.gz: f85cdbe7468884cd9c80d2775a8e30ead5c2102b451bcd1d778e13b2090ceae0
3
+ metadata.gz: ed0f7eae7503218a435d2f704ded8090f173948c680b567931912640b7eee49d
4
+ data.tar.gz: 50246c34f0fd8e7a121a3e321133ddf837773f1a8709ef8d8bc0c2458f281d2d
5
5
  SHA512:
6
- metadata.gz: 523e235dfa63d0f7ba8de9aee74606e13e9795e04ce18893c11298a1b1c2d08d23c03f7e8f3c0c30e297330740abaab6a74fac58b1d0338dec1f8abacecebedd
7
- data.tar.gz: f7eb3b68c618206e5ba488e7eeca347f316e2e5098338eeaebeb06e37b4a37291190a5b98170006daf000f48c21c17aa4ed3c961bd478c6910d9f2de3e73534f
6
+ metadata.gz: f86eb78537de28cbe45c8a2b072486339c402d6e3a485faab22adf4324c5e3c55f8e353af7ef115f5975e04a2645db7911efa9746045c40cdffdcac5527c33b7
7
+ data.tar.gz: 63dfa9792e78efecefd529689becf43555d981a6e1ec2b961b7f12340ef92e2d0e30104c4dcb01fa6e967f79bd629f2c465e74b0a037aee95376226a5983d9eb
data/Gemfile CHANGED
@@ -10,11 +10,7 @@ gem "cheffish", git: "https://github.com/chef/cheffish.git", branch: "main"
10
10
  # Using our fork until they accept it.
11
11
  gem "rest-client", git: "https://github.com/chef/rest-client", branch: "jfm/ucrt_update1"
12
12
 
13
- if RUBY_PLATFORM.include?("mingw") || RUBY_PLATFORM.include?("darwin")
14
- gem "ffi", ">= 1.15.5", "< 1.18.0"
15
- else
16
- gem "ffi", ">= 1.15.5", force_ruby_platform: true
17
- end
13
+ gem "ffi", ">= 1.15.5", force_ruby_platform: true
18
14
 
19
15
  gem "chef-utils", path: File.expand_path("chef-utils", __dir__) if File.exist?(File.expand_path("chef-utils", __dir__))
20
16
  gem "chef-config", path: File.expand_path("chef-config", __dir__) if File.exist?(File.expand_path("chef-config", __dir__))
@@ -28,23 +24,23 @@ if File.exist?(File.expand_path("chef-bin", __dir__))
28
24
  # bundling in a git checkout
29
25
  gem "chef-bin", path: File.expand_path("chef-bin", __dir__)
30
26
  else
31
- # bundling in omnibus
27
+ # bundling in packaging
32
28
  gem "chef-bin" # rubocop:disable Bundler/DuplicatedGem
33
29
  end
34
30
 
35
- group(:omnibus_package) do
31
+ group(:packaging) do
36
32
  gem "appbundler"
37
33
  gem "rb-readline"
38
34
  gem "inspec-core-bin", "= 7.0.107" # need to provide the binaries for inspec
39
35
  gem "chef-vault"
40
36
  end
41
37
 
42
- gem "repl_type_completor", "~> 0.1.12" # deprecation warnings in chef-shell
38
+ gem "repl_type_completor", "~> 0.1.15" # deprecation warnings in chef-shell
43
39
 
44
- group(:omnibus_package, :pry) do
40
+ group(:packaging, :pry) do
45
41
  # Locked because pry-byebug is broken with 13+.
46
42
  # some work is ongoing? https://github.com/deivid-rodriguez/pry-byebug/issues/343
47
- gem "pry", "~> 0.15.2"
43
+ gem "pry", "~> 0.16.0"
48
44
  # byebug does not install on freebsd on ruby 3.0
49
45
  install_if -> { !RUBY_PLATFORM.match?(/freebsd/i) } do
50
46
  gem "pry-byebug"
@@ -59,11 +55,6 @@ group(:ruby_shadow) do
59
55
  end
60
56
  end
61
57
 
62
- # deps that cannot be put in the knife gem because they require a compiler and fail on windows nodes
63
- group(:knife_windows_deps) do
64
- gem "ed25519", "~> 1.2" # ed25519 ssh key support
65
- end
66
-
67
58
  group(:development, :test) do
68
59
  gem "rake", ">= 12.3.3"
69
60
  gem "rspec"
@@ -76,4 +67,8 @@ instance_eval(ENV["GEMFILE_MOD"]) if ENV["GEMFILE_MOD"]
76
67
 
77
68
  # If you want to load debugging tools into the bundle exec sandbox,
78
69
  # add these additional dependencies into Gemfile.local
79
- eval_gemfile("./Gemfile.local") if File.exist?("./Gemfile.local")
70
+ #
71
+ # But doing eval_gemfile("./Gemfile.local") breaks dependabot, so a
72
+ # bit of indirection here
73
+ local_gemfile = File.join(__dir__, "Gemfile.local")
74
+ eval(File.read(local_gemfile)) if File.exist?(local_gemfile)
data/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # Chef Infra
2
- [![Code Climate](https://codeclimate.com/github/chef/chef.svg)](https://codeclimate.com/github/chef/chef)
2
+
3
3
  [![Build Status](https://badge.buildkite.com/c82093430ceec7d27af05febb9dcafe3aa331fff9d74c0ab9d.svg?branch=main)](https://buildkite.com/chef-oss/chef-chef-main-verify)
4
+ [![lint](https://github.com/chef/chef/actions/workflows/lint.yml/badge.svg)](https://github.com/chef/chef/actions/workflows/lint.yml)
5
+ [![kitchen](https://github.com/chef/chef/actions/workflows/kitchen.yml/badge.svg)](https://github.com/chef/chef/actions/workflows/kitchen.yml)
6
+ [![unit_specs](https://github.com/chef/chef/actions/workflows/unit_specs.yml/badge.svg)](https://github.com/chef/chef/actions/workflows/unit_specs.yml)
7
+ [![func_spec](https://github.com/chef/chef/actions/workflows/func_spec.yml/badge.svg)](https://github.com/chef/chef/actions/workflows/func_spec.yml)
4
8
  [![Gem Version](https://badge.fury.io/rb/chef.svg)](https://badge.fury.io/rb/chef)
5
9
  [![](https://img.shields.io/badge/Release%20Policy-Cadence%20Release-brightgreen.svg)](https://github.com/chef/chef/blob/main/docs/dev/design_documents/client_release_cadence.md)
10
+ [![Dependabot Updates](https://github.com/chef/chef/actions/workflows/dependabot/dependabot-updates/badge.svg)](https://github.com/chef/chef/actions/workflows/dependabot/dependabot-updates)
6
11
 
7
12
  **Umbrella Project**: [Chef Infra](https://github.com/chef/chef-oss-practices/blob/main/projects/chef-infra.md)
8
13
 
data/Rakefile CHANGED
@@ -25,6 +25,7 @@ begin
25
25
  require_relative "tasks/dependencies"
26
26
  require_relative "tasks/docs"
27
27
  require_relative "tasks/spellcheck"
28
+ require_relative "tasks/target_mode"
28
29
  require_relative "chef-utils/lib/chef-utils/dist" unless defined?(ChefUtils::Dist)
29
30
  rescue LoadError => e
30
31
  puts "Skipping missing rake dep: #{e}"
@@ -1,4 +1,11 @@
1
- gemspec = instance_eval(File.read(File.expand_path("chef.gemspec", __dir__)))
1
+ # rubocop:disable Chef/Ruby/GemspecLicense
2
+ # License is in the included gemspec.
3
+ gemspec = Gem::Specification.load(File.expand_path("chef.gemspec", __dir__))
4
+
5
+ # In situations like Dependabot, the above returns nil, so create an empty
6
+ # gemspec so that the rest of this file doesn't error out trying to call
7
+ # methods on nil
8
+ gemspec = Gem::Specification.new unless gemspec
2
9
 
3
10
  gemspec.platform = Gem::Platform.new(%w{universal mingw-ucrt})
4
11
 
@@ -6,7 +13,7 @@ gemspec.add_dependency "win32-api", "~> 1.10.0"
6
13
  gemspec.add_dependency "win32-event", "~> 0.6.1"
7
14
  # TODO: Relax this pin and make the necessary updaets. The issue originally
8
15
  # leading to this pin has been fixed in 0.6.5.
9
- gemspec.add_dependency "win32-eventlog", "0.6.3"
16
+ gemspec.add_dependency "win32-eventlog", "0.6.7"
10
17
  gemspec.add_dependency "win32-mmap", "~> 0.4.1"
11
18
  gemspec.add_dependency "win32-mutex", "~> 0.4.2"
12
19
  gemspec.add_dependency "win32-process", "~> 0.9"
data/chef.gemspec CHANGED
@@ -10,8 +10,17 @@ if File.exist?(vs_path)
10
10
  $: << File.join(file_directory, "chef-utils", "lib")
11
11
  end
12
12
  # if the path doesn't exist then we're just in the wild gem and not in the git repo
13
- require "chef-utils/version_string"
14
- require "chef/version"
13
+ begin
14
+ require "chef-utils/version_string"
15
+ require "chef/version"
16
+ rescue LoadError
17
+ # lib/ is not available in all contexts (e.g. Dependabot security scans
18
+ # fetch only gemspec files, not the full repo). Provide a stub so the
19
+ # gemspec can still be evaluated to inspect its dependencies.
20
+ module Chef
21
+ VERSION = "0.0.0".freeze
22
+ end
23
+ end
15
24
 
16
25
  Gem::Specification.new do |s|
17
26
  s.name = "chef"
@@ -25,10 +34,11 @@ Gem::Specification.new do |s|
25
34
  s.email = "adam@chef.io"
26
35
  s.homepage = "https://www.chef.io"
27
36
 
37
+ # help dependabot by specifying a default. If the default is in
38
+ # the 'else', dependabot misses it and falls back to a very old ruby
39
+ s.required_ruby_version = ">= 3.1.0"
28
40
  if RUBY_PLATFORM =~ /aix/
29
41
  s.required_ruby_version = ">= 3.0.3"
30
- else
31
- s.required_ruby_version = ">= 3.1.0"
32
42
  end
33
43
 
34
44
  s.add_dependency "chef-config", "= #{Chef::VERSION}"
@@ -41,13 +51,13 @@ Gem::Specification.new do |s|
41
51
  s.add_dependency "mixlib-cli", ">= 2.1.1", "< 3.0"
42
52
  s.add_dependency "mixlib-log", ">= 2.0.3", "< 4.0"
43
53
  s.add_dependency "mixlib-authentication", ">= 2.1", "< 4"
44
- s.add_dependency "mixlib-shellout", "~> 3.3.8"
54
+ s.add_dependency "mixlib-shellout", ">= 3.3.8", "< 3.5.0"
45
55
  s.add_dependency "mixlib-archive", ">= 0.4", "< 2.0"
46
56
  s.add_dependency "ohai", "~> 19.0"
47
57
  s.add_dependency "inspec-core", "~> 7.0.107"
48
58
 
49
59
  s.add_dependency "ffi", ">= 1.15.5", "< 1.18.0"
50
- s.add_dependency "ffi-yajl", "~> 2.2"
60
+ s.add_dependency "ffi-yajl", ">= 2.2", "< 4.0"
51
61
  s.add_dependency "net-sftp", ">= 2.1.2", "< 5.0" # remote_file resource
52
62
  s.add_dependency "net-ftp" # remote_file resource
53
63
  s.add_dependency "ed25519", "~> 1.2" # ssh-ed25519 support for target mode
@@ -65,14 +75,14 @@ Gem::Specification.new do |s|
65
75
  s.add_dependency "csv", "~> 3.3.5" # really needs to come from inspec?
66
76
  s.add_dependency "syslog-logger", "~> 1.6"
67
77
  s.add_dependency "unf_ext", "~> 0.0.9.1" # older platforms
68
- s.add_dependency "uri", "~> 1.0.4" # CVE-2025-61594 fixed in >= 1.0.4
78
+ s.add_dependency "uri", ">= 1.0.4", "< 1.2.0" # CVE-2025-61594 fixed in >= 1.0.4
69
79
  s.add_dependency "corefoundation", "~> 0.3.4" # macos_userdefaults resource
70
80
 
71
81
  s.add_dependency "proxifier2", "~> 1.1"
72
82
 
73
83
  s.add_dependency "aws-sdk-s3", "~> 1.91" # s3 recipe-url support
74
84
  s.add_dependency "aws-sdk-secretsmanager", "~> 1.46"
75
- s.add_dependency "vault", "~> 0.18.2" # hashi vault official client gem
85
+ s.add_dependency "vault", ">= 0.18.2", "< 0.21.0" # hashi vault official client gem
76
86
  s.add_dependency "chef-licensing", "~> 1.3"
77
87
 
78
88
  s.bindir = "bin"
@@ -127,8 +127,14 @@ class Chef::Application::Client < Chef::Application::Base
127
127
  train_config = Train.unpack_target_from_uri(Chef::Config.target_mode.host)
128
128
  Chef::Config.target_mode = train_config
129
129
  end
130
+ # Save the operator's identity BEFORE enabling target mode.
131
+ # client_key must be captured here because its default path changes once
132
+ # target_mode.enabled = true (it would resolve to /etc/chef/<target>/client.pem
133
+ # instead of the workstation's /etc/chef/client.pem).
134
+ Chef::Config[:api_client_name] ||= Chef::Config[:node_name]
135
+ Chef::Config[:api_client_key] ||= Chef::Config[:client_key]
130
136
  Chef::Config.target_mode.enabled = true
131
- Chef::Config.node_name = Chef::Config.target_mode.host unless Chef::Config.node_name
137
+ Chef::Config.node_name = Chef::Config.target_mode.host
132
138
  end
133
139
 
134
140
  if config[:credentials]
@@ -91,7 +91,7 @@ class Chef
91
91
  end
92
92
  end
93
93
  # Create the file itself
94
- container.add_child(CookbookFile.new(parts[parts.length - 1], container, file))
94
+ container.add_child(CookbookFile.new(parts[-1], container, file))
95
95
  end
96
96
  end
97
97
  @children = @children.sort_by(&:name)
data/lib/chef/client.rb CHANGED
@@ -385,8 +385,8 @@ class Chef
385
385
  #
386
386
  # @api private
387
387
  def rest
388
- @rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], client_name: node_name,
389
- signing_key_filename: Chef::Config[:client_key])
388
+ @rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_url], client_name: api_client_name,
389
+ signing_key_filename: api_client_key)
390
390
  end
391
391
 
392
392
  # A rest object with validate_utf8 set to false. This will not throw exceptions
@@ -397,8 +397,26 @@ class Chef
397
397
  # @api private
398
398
  def rest_clean
399
399
  @rest_clean ||=
400
- Chef::ServerAPI.new(Chef::Config[:chef_server_url], client_name: node_name,
401
- signing_key_filename: Chef::Config[:client_key], validate_utf8: false)
400
+ Chef::ServerAPI.new(Chef::Config[:chef_server_url], client_name: api_client_name,
401
+ signing_key_filename: api_client_key, validate_utf8: false)
402
+ end
403
+
404
+ # Returns the client name to use for Chef Server API auth. In target mode the
405
+ # operator's original identity is stored in api_client_name so that API calls
406
+ # authenticate as the workstation client rather than as the target node.
407
+ #
408
+ # @api private
409
+ def api_client_name
410
+ Chef::Config[:api_client_name] || node_name
411
+ end
412
+
413
+ # Returns the signing key path to use for Chef Server API auth. In target mode
414
+ # the operator's original key is stored in api_client_key so that API calls
415
+ # use the workstation key rather than the target node's key.
416
+ #
417
+ # @api private
418
+ def api_client_key
419
+ Chef::Config[:api_client_key] || Chef::Config[:client_key]
402
420
  end
403
421
 
404
422
  #
@@ -77,14 +77,32 @@ class Chef
77
77
  logger.debug("#{self.class}##{__method__}: enabling Compliance Phase")
78
78
 
79
79
  report_with_interval
80
+ @compliance_phase_completed = true
80
81
  end
81
82
 
82
- def run_failed(_exception, _run_status)
83
+ def run_failed(exception, _run_status)
83
84
  # If the run has failed because our own validation of compliance
84
85
  # phase configuration has failed, we don't want to submit a report
85
86
  # because we're still not configured correctly.
86
87
  return unless enabled? && @validation_passed
87
88
 
89
+ # A reboot exception is not a real failure — it is an expected outcome
90
+ # when a reboot resource fires :reboot_now. In that case run_completed
91
+ # has already executed the Compliance Phase, so running it again here
92
+ # would produce a duplicate scan (and the second scan may be killed by
93
+ # the pending reboot).
94
+ if exception.is_a?(Chef::Exceptions::Reboot)
95
+ logger.debug("#{self.class}##{__method__}: skipping Compliance Phase because a reboot was requested")
96
+ return
97
+ end
98
+
99
+ # Guard against any other code path that might trigger both run_completed
100
+ # and run_failed in the same client run.
101
+ if @compliance_phase_completed
102
+ logger.debug("#{self.class}##{__method__}: skipping Compliance Phase because it has already run in this client run")
103
+ return
104
+ end
105
+
88
106
  logger.debug("#{self.class}##{__method__}: enabling Compliance Phase")
89
107
 
90
108
  report_with_interval
@@ -32,7 +32,7 @@ class Chef
32
32
  @events = events
33
33
  end
34
34
 
35
- # Installs the gems into the omnibus gemset.
35
+ # Installs the gems into the gemset.
36
36
  #
37
37
  def install
38
38
  cookbook_gems = Hash.new { |h, k| h[k] = [] }
@@ -130,7 +130,7 @@ class Chef
130
130
  rescue Net::HTTPClientException, Net::HTTPFatalError, Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError => e
131
131
  error_message = "Failed to upload #{file} (#{checksum}) to #{url} : #{e.message}"
132
132
  error_message << "\n#{e.response.body}" if e.respond_to?(:response)
133
- Chef::Knife.ui.error(error_message)
133
+ Chef::Log.error(error_message)
134
134
  raise
135
135
  end
136
136
  end
@@ -24,6 +24,14 @@ class Chef
24
24
  # when a custom resource uses the 'core::rest_resource' partial.
25
25
  #
26
26
  module RestResource
27
+ private
28
+
29
+ def inherited_accessor(name)
30
+ superclass.public_send(name) if superclass.respond_to?(name)
31
+ end
32
+
33
+ public
34
+
27
35
  # Define property mapping between resource properties and JSON API fields
28
36
  #
29
37
  # Maps resource properties to their corresponding locations in the JSON
@@ -56,12 +64,12 @@ class Chef
56
64
  # @see #json_to_property Method that uses this mapping to extract values
57
65
  # @see #property_to_json Method that uses this mapping to create JSON
58
66
  def rest_property_map(rest_property_map = NOT_PASSED)
59
- if rest_property_map != NOT_PASSED
67
+ unless rest_property_map.equal?(NOT_PASSED)
60
68
  rest_property_map = rest_property_map.to_h { |k| [k.to_sym, k] } if rest_property_map.is_a? Array
61
69
 
62
70
  @rest_property_map = rest_property_map
63
71
  end
64
- @rest_property_map
72
+ @rest_property_map || inherited_accessor(:rest_property_map)
65
73
  end
66
74
 
67
75
  # Define the REST API collection URL
@@ -83,13 +91,13 @@ class Chef
83
91
  # # GET /api/v1/users # List all users
84
92
  # # POST /api/v1/users # Create new user
85
93
  def rest_api_collection(rest_api_collection = NOT_PASSED)
86
- if rest_api_collection != NOT_PASSED
94
+ unless rest_api_collection.equal?(NOT_PASSED)
87
95
  raise ArgumentError, "You must pass an absolute path to rest_api_collection" unless rest_api_collection.start_with? "/"
88
96
 
89
97
  @rest_api_collection = rest_api_collection
90
98
  end
91
99
 
92
- @rest_api_collection
100
+ @rest_api_collection || inherited_accessor(:rest_api_collection)
93
101
  end
94
102
 
95
103
  # Define the REST API document URL with RFC 6570 template support
@@ -131,13 +139,15 @@ class Chef
131
139
  #
132
140
  # @see https://tools.ietf.org/html/rfc6570 RFC 6570 URI Template specification
133
141
  def rest_api_document(rest_api_document = NOT_PASSED, first_element_only: false)
134
- if rest_api_document != NOT_PASSED
142
+ unless rest_api_document.equal?(NOT_PASSED)
135
143
  raise ArgumentError, "You must pass an absolute path to rest_api_document" unless rest_api_document.start_with? "/"
136
144
 
137
145
  @rest_api_document = rest_api_document
138
146
  @rest_api_document_first_element_only = first_element_only
139
147
  end
140
- @rest_api_document
148
+ @rest_api_document ||
149
+ inherited_accessor(:rest_api_document) ||
150
+ (rest_api_collection && rest_identity_property ? "#{rest_api_collection}/{#{rest_identity_property}}" : nil)
141
151
  end
142
152
 
143
153
  # Define explicit identity mapping for resource identification
@@ -177,8 +187,8 @@ class Chef
177
187
  # 'organization.id' => :org_id
178
188
  # })
179
189
  def rest_identity_map(rest_identity_map = NOT_PASSED)
180
- @rest_identity_map = rest_identity_map if rest_identity_map != NOT_PASSED
181
- @rest_identity_map
190
+ @rest_identity_map = rest_identity_map unless rest_identity_map.equal?(NOT_PASSED)
191
+ @rest_identity_map || inherited_accessor(:rest_identity_map)
182
192
  end
183
193
 
184
194
  # Declare properties that should only be sent during resource creation
@@ -219,17 +229,58 @@ class Chef
219
229
  # # Initialization parameters
220
230
  # rest_post_only_properties [:template_id, :source_snapshot]
221
231
  def rest_post_only_properties(rest_post_only_properties = NOT_PASSED)
222
- if rest_post_only_properties != NOT_PASSED
232
+ unless rest_post_only_properties.equal?(NOT_PASSED)
223
233
  @rest_post_only_properties = Array(rest_post_only_properties).map(&:to_sym)
224
234
  end
225
- @rest_post_only_properties || []
235
+ @rest_post_only_properties || inherited_accessor(:rest_post_only_properties) || []
226
236
  end
227
237
 
228
238
  def rest_api_document_first_element_only(rest_api_document_first_element_only = NOT_PASSED)
229
- if rest_api_document_first_element_only != NOT_PASSED
239
+ unless rest_api_document_first_element_only.equal?(NOT_PASSED)
230
240
  @rest_api_document_first_element_only = rest_api_document_first_element_only
231
241
  end
232
- @rest_api_document_first_element_only
242
+ @rest_api_document_first_element_only || inherited_accessor(:rest_api_document_first_element_only)
243
+ end
244
+
245
+ # Define the base URL for the REST API
246
+ #
247
+ # Sets the base endpoint URL that is prepended to all collection and document
248
+ # URLs. This allows resource definitions to be self-contained without requiring
249
+ # the Train transport endpoint to be pre-configured.
250
+ #
251
+ # @param rest_api_endpoint [String, NOT_PASSED] The base URL of the REST API
252
+ # - NOT_PASSED: Acts as getter, returns current endpoint URL
253
+ #
254
+ # @return [String, nil] The current endpoint URL
255
+ #
256
+ # @example
257
+ # rest_api_endpoint "https://api.example.com"
258
+ # rest_api_collection "/api/v1/users"
259
+ # # GET https://api.example.com/api/v1/users
260
+ def rest_api_endpoint(rest_api_endpoint = NOT_PASSED)
261
+ @rest_api_endpoint = rest_api_endpoint unless rest_api_endpoint.equal?(NOT_PASSED)
262
+ @rest_api_endpoint || inherited_accessor(:rest_api_endpoint)
263
+ end
264
+
265
+ # Declare the property that uniquely identifies a resource in the REST API
266
+ #
267
+ # Sets the identity property for the resource and auto-generates the document
268
+ # URL as "#{rest_api_collection}/{property}" when no explicit rest_api_document
269
+ # is provided. This is a convenience alternative to setting rest_api_document
270
+ # manually.
271
+ #
272
+ # @param property [Symbol, NOT_PASSED] The property name used as the resource identifier
273
+ # - NOT_PASSED: Acts as getter, returns current identity property
274
+ #
275
+ # @return [Symbol, nil] The current identity property name
276
+ #
277
+ # @example
278
+ # rest_api_collection "/api/v1/users"
279
+ # rest_identity_property :username
280
+ # # Auto-generates rest_api_document as "/api/v1/users/{username}"
281
+ def rest_identity_property(property = NOT_PASSED)
282
+ @rest_identity_property = property unless property.equal?(NOT_PASSED)
283
+ @rest_identity_property || inherited_accessor(:rest_identity_property)
233
284
  end
234
285
 
235
286
  end
@@ -33,6 +33,12 @@ class Chef
33
33
  module ClassMethods
34
34
  # We want to mix these in as class methods
35
35
  def writable?(path)
36
+ # In target mode the path refers to the remote filesystem. Delegate
37
+ # to TargetIO so the check runs on the remote host via SSH rather than
38
+ # against the local Windows filesystem (where Linux paths like /tmp do
39
+ # not exist and ::File.exist? always returns false).
40
+ return ::TargetIO::File.writable?(path) if Chef::Config.target_mode?
41
+
36
42
  ::File.exist?(path) && Chef::ReservedNames::Win32::File.file_access_check(
37
43
  path, Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE
38
44
  )
@@ -26,11 +26,13 @@ class Chef
26
26
  # the values specified by a value object, usually a Chef::Resource.
27
27
  class FileAccessControl
28
28
 
29
+ require_relative "file_access_control/unix"
30
+
29
31
  if RUBY_PLATFORM.match?(/mswin|mingw|windows/)
30
32
  require_relative "file_access_control/windows"
33
+
31
34
  include FileAccessControl::Windows
32
35
  else
33
- require_relative "file_access_control/unix"
34
36
  include FileAccessControl::Unix
35
37
  end
36
38
 
@@ -55,6 +57,15 @@ class Chef
55
57
  @current_resource, @resource, @provider = current_resource, new_resource, provider
56
58
  @file = @current_resource.path
57
59
  @modified = false
60
+
61
+ # When running on Windows in target mode the remote host is a Unix
62
+ # system managed via SSH. Extend this instance with the Unix access
63
+ # control module so that permissions are applied via TargetIO
64
+ # (chmod/chown over SSH) rather than Win32 security APIs against a
65
+ # path that does not exist on the local Windows filesystem.
66
+ if RUBY_PLATFORM.match?(/mswin|mingw|windows/) && ChefConfig::Config.target_mode?
67
+ extend FileAccessControl::Unix
68
+ end
58
69
  end
59
70
 
60
71
  def modified?
@@ -30,13 +30,13 @@ class Chef
30
30
 
31
31
  def report
32
32
  if count == 0
33
- puts "\nNo resources to profile\n\n"
33
+ Chef::Log.info("No resources to profile")
34
34
  return
35
35
  end
36
36
 
37
37
  top = all_records.sort_by(&:elapsed_time).last(amount).reverse
38
38
  data = top.map { |r| [ r.new_resource.to_s, r.elapsed_time, r.action, r.new_resource.cookbook_name, r.new_resource.recipe_name, stripped_source_line(r.new_resource) ] }
39
- puts "\nTop #{count} slowest #{count == 1 ? "resource" : "resources"}:\n\n"
39
+ Chef::Log.info("Top #{count} slowest #{count == 1 ? "resource" : "resources"}:")
40
40
  table = TTY::Table.new(%w{resource elapsed_time action cookbook recipe source}, data)
41
41
  rendered = table.render do |renderer|
42
42
  renderer.border do
@@ -44,8 +44,7 @@ class Chef
44
44
  mid_mid " "
45
45
  end
46
46
  end
47
- puts rendered
48
- puts "\n"
47
+ Chef::Log.info(rendered)
49
48
  end
50
49
 
51
50
  def all_records
@@ -9,12 +9,10 @@ class Chef
9
9
  class << self
10
10
  def fetch_and_persist
11
11
  Chef::Log.info "Fetching and persisting license..."
12
- # This is a temporary arrangement to continue end-to-end recipe testing of Chef.
13
- # Windows, Mac, Linux VMs on vagrant will be tested using legacy test-kitchen
14
- # whereas Linux docker containers will be tested using chef-test-kitchen-enterprise (habitat)
15
- # Reason: chef-test-kitchen-enterprise currently supports only kitchen-dokken driver and is available only for Linux x86_64
16
- # So for now we skip license validation on Mac, Windows, Linux distributions which run using test kitchen.
17
- if ENV["TEST_KITCHEN"] && !ChefUtils.docker?
12
+ # Skip license validation in CI/testing environments.
13
+ # This covers GitHub Actions, Buildkite, Test Kitchen, and generic CI.
14
+ # Licensing determination is handled by ChefLicensing directly.
15
+ if skip_license_validation?
18
16
  Chef::Log.info "****Skipping license validation..."
19
17
  return
20
18
  end
@@ -32,6 +30,12 @@ class Chef
32
30
 
33
31
  def check_software_entitlement!
34
32
  Chef::Log.info "Checking software entitlement..."
33
+ # Skip entitlement check in the same environments where fetch_and_persist is skipped,
34
+ # since no license keys would have been persisted in those environments.
35
+ if skip_license_validation?
36
+ Chef::Log.info "****Skipping software entitlement check..."
37
+ return
38
+ end
35
39
  ChefLicensing.check_software_entitlement!
36
40
  rescue ChefLicensing::SoftwareNotEntitled
37
41
  Chef::Log.error "License is not entitled to use Chef Infra."
@@ -97,6 +101,22 @@ class Chef
97
101
  Chef::Log.error e.message
98
102
  Chef::Application.exit! "Usage error", 1 # Generic failure
99
103
  end
104
+
105
+ private
106
+
107
+ # Returns true when license validation should be skipped.
108
+ # Skips in CI environments (GitHub Actions, Buildkite, Test Kitchen, generic CI).
109
+ # Does NOT skip when running inside Docker containers managed by Test Kitchen
110
+ # (those are tested separately via chef-test-kitchen-enterprise).
111
+ # CHEF_LICENSE acceptance values are handled by ChefLicensing directly.
112
+ def skip_license_validation?
113
+ ci_environment?
114
+ end
115
+
116
+ def ci_environment?
117
+ (ENV["TEST_KITCHEN"] || ENV["GITHUB_ACTIONS"] || ENV["BUILDKITE"] || ENV["CI"]) &&
118
+ !ChefUtils.docker?
119
+ end
100
120
  end
101
121
 
102
122
  class EntitlementError < StandardError
data/lib/chef/node.rb CHANGED
@@ -650,9 +650,21 @@ class Chef
650
650
 
651
651
  # Load a node by name
652
652
  def self.load(name)
653
- from_hash(Chef::ServerAPI.new(Chef::Config[:chef_server_url]).get("nodes/#{name}"))
653
+ from_hash(Chef::ServerAPI.new(Chef::Config[:chef_server_url],
654
+ client_name: api_client_name,
655
+ signing_key_filename: api_client_key).get("nodes/#{name}"))
654
656
  end
655
657
 
658
+ def self.api_client_name
659
+ Chef::Config[:api_client_name] || Chef::Config[:node_name]
660
+ end
661
+ private_class_method :api_client_name
662
+
663
+ def self.api_client_key
664
+ Chef::Config[:api_client_key] || Chef::Config[:client_key]
665
+ end
666
+ private_class_method :api_client_key
667
+
656
668
  # Remove this node via the REST API
657
669
  def destroy
658
670
  chef_server_rest.delete("nodes/#{name}")
@@ -248,13 +248,24 @@ class Chef
248
248
  end
249
249
 
250
250
  def api_service
251
- @api_service ||= Chef::ServerAPI.new(config[:chef_server_url], version_class: Chef::CookbookManifestVersions )
251
+ @api_service ||= Chef::ServerAPI.new(config[:chef_server_url],
252
+ client_name: api_client_name,
253
+ signing_key_filename: api_client_key,
254
+ version_class: Chef::CookbookManifestVersions)
252
255
  end
253
256
 
254
257
  def config
255
258
  Chef::Config
256
259
  end
257
260
 
261
+ def api_client_name
262
+ config[:api_client_name] || config[:node_name]
263
+ end
264
+
265
+ def api_client_key
266
+ config[:api_client_key] || config[:client_key]
267
+ end
268
+
258
269
  end
259
270
  end
260
271
  end
@@ -515,6 +515,8 @@ class Chef
515
515
  # @api private
516
516
  def api_service
517
517
  @api_service ||= Chef::ServerAPI.new(config[:chef_server_url],
518
+ client_name: api_client_name,
519
+ signing_key_filename: api_client_key,
518
520
  version_class: Chef::CookbookManifestVersions)
519
521
  end
520
522
 
@@ -523,6 +525,16 @@ class Chef
523
525
  Chef::Config
524
526
  end
525
527
 
528
+ # @api private
529
+ def api_client_name
530
+ config[:api_client_name] || config[:node_name]
531
+ end
532
+
533
+ # @api private
534
+ def api_client_key
535
+ config[:api_client_key] || config[:client_key]
536
+ end
537
+
526
538
  # Indicates whether the policy is temporary, which means an
527
539
  # override_runlist was provided. Chef::Client uses this to decide whether
528
540
  # to do the final node save at the end of the run or not.