chef-config 18.10.17 → 19.1.164

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f25d6c5f5a6ed06c65ffd04ce45e7c086fc4efa2ed7dbfb970e6941ab09bb4a
4
- data.tar.gz: 8e3af92f55a0cd3d345dcf5137cca5cfc5ea2493c85efbe68510421aca98793b
3
+ metadata.gz: 309b22099435d14e4f8be71a98aff1005772ac64dbf60c440e5d33f4fce10d53
4
+ data.tar.gz: 8c46f82f7c17fdb2330ca0f2182f9e9476b3d7f96c18b13728d8749d1b86831a
5
5
  SHA512:
6
- metadata.gz: 823eb67bb06058cf1bd535f20cb01917dcc2e16b0bc52a77895c4bb2c0fd38a21a25638f09921042e8287a7a56101409fba407fe2549c539b9c5679abd16377d
7
- data.tar.gz: fba1c92cb4db7c997707d11871b744dd79b73d37d63640dd3b1c3fc1907e3e8f6f871b3e47a276a99b16522e225a5c5538002c47c5f23feae3a870f43ef5e6d5
6
+ metadata.gz: c822ebbe0118452e6240c8d1c0818f90a13701694fa3d93fd700d123ce8150130ab911d076409c76989928678c0d3e04326c367499fc3bbea6902239833d8521
7
+ data.tar.gz: 968e6d6a478e2ec8ef34fe3651fb9c7badc6c1b6a5bf59c832a916ee3d48a398104fe6f64c47f275d082c72c609158b3bfdda802f1ced9de4bc1c3de9f9ffab5
data/chef-config.gemspec CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "fuzzyurl"
31
31
  spec.add_dependency "addressable"
32
32
  spec.add_dependency "tomlrb", "~> 1.2"
33
+ spec.add_dependency "racc"
33
34
 
34
35
  spec.files = %w{Rakefile LICENSE} + Dir.glob("*.gemspec") +
35
36
  Dir.glob("{lib,spec}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
@@ -4,7 +4,7 @@
4
4
  # Author:: AJ Christensen (<aj@chef.io>)
5
5
  # Author:: Mark Mzyk (<mmzyk@chef.io>)
6
6
  # Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
7
- # Copyright:: Copyright (c) Chef Software Inc.
7
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
8
8
  # License:: Apache License, Version 2.0
9
9
  #
10
10
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -421,7 +421,14 @@ module ChefConfig
421
421
  # If your `file_cache_path` resides on a NFS (or non-flock()-supporting
422
422
  # fs), it's recommended to set this to something like
423
423
  # '/tmp/chef-client-running.pid'
424
- default(:lockfile) { PathHelper.join(file_cache_path, "#{ChefUtils::Dist::Infra::CLIENT}-running.pid") }
424
+ # In Target Mode, the node name will be used as a prefix to allow
425
+ # parallel execution of Chef against different targets
426
+ default(:lockfile) do
427
+ prefix = ""
428
+ prefix = "#{ChefConfig::Config.node_name}-" if ChefConfig::Config.target_mode?
429
+
430
+ PathHelper.join(file_cache_path, "#{prefix}#{ChefUtils::Dist::Infra::CLIENT}-running.pid")
431
+ end
425
432
 
426
433
  ## Daemonization Settings ##
427
434
  # What user should Chef run as?
@@ -917,7 +924,11 @@ module ChefConfig
917
924
  default :profile, nil
918
925
 
919
926
  default :chef_guid_path do
920
- PathHelper.join(config_dir, "#{ChefUtils::Dist::Infra::SHORT}_guid")
927
+ if target_mode?
928
+ PathHelper.join(config_dir, target_mode.host, "#{ChefUtils::Dist::Infra::SHORT}_guid")
929
+ else
930
+ PathHelper.join(config_dir, "#{ChefUtils::Dist::Infra::SHORT}_guid")
931
+ end
921
932
  end
922
933
 
923
934
  default :chef_guid, nil
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,5 +23,8 @@ module ChefConfig
23
23
  class ConfigurationError < ArgumentError; end
24
24
  class InvalidPath < StandardError; end
25
25
  class UnparsableConfigOption < StandardError; end
26
+ class NoCredentialsFound < StandardError; end
26
27
 
28
+ class UnsupportedSecretsProvider < ConfigurationError; end
29
+ class UnresolvedSecret < ConfigurationError; end
27
30
  end
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Matt Wrock (<matt@mattwrock.com>)
3
- # Copyright:: Copyright (c) Chef Software Inc.
3
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,6 +26,8 @@ module ChefConfig
26
26
  # @since 13.7
27
27
  # @api internal
28
28
  module Credentials
29
+ attr_reader :credentials_config
30
+
29
31
  # Compute the active credentials profile name.
30
32
  #
31
33
  # The lookup order is argument (from --profile), environment variable
@@ -54,6 +56,11 @@ module ChefConfig
54
56
  # @since 14.4
55
57
  # @return [String]
56
58
  def credentials_file_path
59
+ return Chef::Config[:credentials] if defined?(Chef::Config) && Chef::Config.key?(:credentials)
60
+
61
+ env_file = ENV["CHEF_CREDENTIALS_FILE"]
62
+ return env_file if env_file && File.file?(env_file)
63
+
57
64
  PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR, "credentials").freeze
58
65
  end
59
66
 
@@ -68,7 +75,7 @@ module ChefConfig
68
75
  return nil unless File.file?(credentials_file)
69
76
 
70
77
  begin
71
- Tomlrb.load_file(credentials_file)
78
+ @credentials_config = Tomlrb.load_file(credentials_file)
72
79
  rescue => e
73
80
  # TOML's error messages are mostly rubbish, so we'll just give a generic one
74
81
  message = "Unable to parse Credentials file: #{credentials_file}\n"
@@ -85,17 +92,129 @@ module ChefConfig
85
92
  # @return [void]
86
93
  def load_credentials(profile = nil)
87
94
  profile = credentials_profile(profile)
88
- cred_config = parse_credentials_file
89
- return if cred_config.nil? # No credentials, nothing to do here.
90
95
 
91
- if cred_config[profile].nil?
96
+ parse_credentials_file
97
+ return if credentials_config.nil? # No credentials, nothing to do here.
98
+
99
+ if credentials_config[profile].nil?
92
100
  # Unknown profile name. For "default" just silently ignore, otherwise
93
101
  # raise an error.
94
102
  return if profile == "default"
95
103
 
96
104
  raise ChefConfig::ConfigurationError, "Profile #{profile} doesn't exist. Please add it to #{credentials_file_path}."
97
105
  end
98
- apply_credentials(cred_config[profile], profile)
106
+
107
+ resolve_secrets(profile)
108
+
109
+ apply_credentials(credentials_config[profile], profile)
110
+ end
111
+
112
+ GLOBAL_CONFIG_HASHES = %w{ default_secrets_provider }.freeze
113
+
114
+ # Extract global (non-profile) settings from credentials file.
115
+ #
116
+ # @since 19.1
117
+ # @return [Hash]
118
+ def global_options
119
+ globals = credentials_config.filter { |_, v| v.is_a? String }
120
+ globals.merge! credentials_config.filter { |k, _| GLOBAL_CONFIG_HASHES.include? k }
121
+ end
122
+
123
+ SUPPORTED_SECRETS_PROVIDERS = %w{ hashicorp-vault }.freeze
124
+
125
+ # Resolve all secrets in a credentials file
126
+ #
127
+ # @since 19.1
128
+ # @param profile [String] Profile to resolve secrets in.
129
+ # @return [Hash]
130
+ def resolve_secrets(profile)
131
+ return unless credentials_config
132
+ raise NoCredentialsFound.new("No credentials found for profile '#{profile}'") unless credentials_config[profile]
133
+
134
+ secrets = credentials_config[profile].filter { |k, v| v.is_a?(Hash) && v.keys.include?("secret") }
135
+ return if secrets.empty?
136
+
137
+ secrets.each do |option, secrets_config|
138
+ unless valid_secrets_provider?(secrets_config)
139
+ raise UnsupportedSecretsProvider.new("Unsupported credentials secrets provider on '#{option}' for profile '#{profile}'")
140
+ end
141
+
142
+ secrets_config.merge!(default_secrets_provider)
143
+
144
+ logger.debug("Resolving credentials secret '#{option}' for profile '#{profile}'")
145
+ begin
146
+ resolved_value = resolve_secret(secrets_config)
147
+ ensure
148
+ raise UnresolvedSecret.new("Could not resolve secret '#{option}' for profile '#{profile}'") if resolved_value.nil?
149
+ end
150
+
151
+ credentials_config[profile][option] = resolved_value
152
+ end
153
+ end
154
+
155
+ # Check, if referenced secrets provider is supported.
156
+ #
157
+ # @since 19.1
158
+ # @param secrets_config [Hash] Parsed contents of a secret in a profile.
159
+ # @return [true, false]
160
+ def valid_secrets_provider?(secrets_config)
161
+ provider_config = secrets_config["secrets_provider"] || default_secrets_provider
162
+ provider = provider_config["name"]
163
+
164
+ provider && SUPPORTED_SECRETS_PROVIDERS.include?(provider)
165
+ end
166
+
167
+ def default_secrets_provider
168
+ global_options["default_secrets_provider"]
169
+ end
170
+
171
+ # Resolve a specific secret.
172
+ #
173
+ # To be replaced later by a Train-like framework to support multiple backends.
174
+ #
175
+ # @since 19.1
176
+ # @param secrets_config [Hash] Parsed contents of a secret in a profile.
177
+ # @return [String]
178
+ def resolve_secret(secrets_config)
179
+ resolve_secret_hashicorp(secrets_config)
180
+ end
181
+
182
+ # Resolver logic for Hashicorp Vault.
183
+ #
184
+ # Local lazy loading of Gems which are not part of chef-config or chef-utils,
185
+ # but chef itself to be switched by a unified secrets mechanism for credentials
186
+ # and Chef DSL later. Showstopper mitigation for 19 GA.
187
+ #
188
+ # @since 19.1
189
+ # @param secrets_config [Hash] Parsed contents of a secret in a profile.
190
+ # @return [String]
191
+ def resolve_secret_hashicorp(secrets_config)
192
+ vault_config = secrets_config.transform_keys(&:to_sym)
193
+ vault_config[:address] = vault_config[:endpoint]
194
+
195
+ # Lazy require due to Gem being part of Chef and rarely used functionality
196
+ require "vault" unless defined? Vault
197
+ @vault ||= Vault::Client.new(vault_config)
198
+
199
+ secret = secrets_config["secret"]
200
+ engine = vault_config[:engine] || "secret"
201
+ engine_type = vault_config[:engine_type] || "kv2"
202
+ secret_value = case engine_type
203
+ when "kv", "kv1"
204
+ @vault.logical.read("#{engine_type}/#{secret}")
205
+ when "kv2"
206
+ @vault.kv(engine).read(secret)&.data
207
+ else
208
+ raise UnsupportedSecretsProvider.new("No support for secrets engine #{engine_type}")
209
+ end
210
+
211
+ # Always JSON for Hashicorp Vault, but this is future compatible to other providers
212
+ if secret_value.is_a?(Hash)
213
+ require "jmespath" unless defined? ::JMESPath
214
+ ::JMESPath.search(secrets_config["field"], secret_value)
215
+ else
216
+ secret_value
217
+ end
99
218
  end
100
219
  end
101
220
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,5 +1,5 @@
1
1
  # Author:: Bryan McLellan <btm@loftninjas.org>
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,15 +36,17 @@ module ChefConfig
36
36
  #
37
37
  def load_credentials(profile)
38
38
  # Tomlrb.load_file returns a hash with keys as strings
39
- credentials = parse_credentials_file
40
- if contains_split_fqdn?(credentials, profile)
39
+ credentials_config = parse_credentials_file
40
+ if contains_split_fqdn?(credentials_config, profile)
41
41
  logger.warn("Credentials file #{credentials_file_path} contains target '#{profile}' as a Hash, expected a string.")
42
42
  logger.warn("Hostnames must be surrounded by single quotes, e.g. ['host.example.org']")
43
43
  end
44
44
 
45
+ resolve_secrets(profile)
46
+
45
47
  # host names must be specified in credentials file as ['foo.example.org'] with quotes
46
- if !credentials.nil? && !credentials[profile].nil?
47
- credentials[profile].transform_keys(&:to_sym) # return symbolized keys to match Train.options()
48
+ if !credentials_config.nil? && !credentials_config[profile].nil?
49
+ credentials_config[profile].transform_keys(&:to_sym) # return symbolized keys to match Train.options()
48
50
  else
49
51
  nil
50
52
  end
@@ -59,6 +61,8 @@ module ChefConfig
59
61
  # This will be a common mistake so we should catch it
60
62
  #
61
63
  def contains_split_fqdn?(hash, fqdn)
64
+ return unless fqdn.include?(".")
65
+
62
66
  fqdn.split(".").reduce(hash) do |h, k|
63
67
  v = h[k]
64
68
  if Hash === v
@@ -74,21 +78,25 @@ module ChefConfig
74
78
  #
75
79
  # Credentials file preference:
76
80
  #
77
- # 1) target_mode.credentials_file
78
- # 2) /etc/chef/TARGET_MODE_HOST/credentials
79
- # 3) #credentials_file_path from parent ($HOME/.chef/credentials)
81
+ # 1) environment variable CHEF_CREDENTIALS_FILE
82
+ # 2) target_mode.credentials_file
83
+ # 3) /etc/chef/TARGET_MODE_HOST/credentials
84
+ # 4) user configuration ($HOME/.chef/target_credentials)
80
85
  #
81
86
  def credentials_file_path
82
87
  tm_config = config.target_mode
83
88
  profile = tm_config.host
84
89
 
90
+ env_file = ENV["CHEF_CREDENTIALS_FILE"]
85
91
  credentials_file =
86
- if tm_config.credentials_file && File.exist?(tm_config.credentials_file)
92
+ if env_file && File.exist?(env_file)
93
+ env_file
94
+ elsif tm_config.credentials_file && File.exist?(tm_config.credentials_file)
87
95
  tm_config.credentials_file
88
96
  elsif File.exist?(config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials"))
89
97
  config.platform_specific_path("#{ChefConfig::Config.etc_chef_dir}/#{profile}/credentials")
90
98
  else
91
- super
99
+ PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR, "target_credentials").freeze
92
100
  end
93
101
 
94
102
  raise ArgumentError, "No credentials file found for target '#{profile}'" unless credentials_file
@@ -112,7 +120,7 @@ module ChefConfig
112
120
  # Load the credentials file, and place any valid settings into the train configuration
113
121
  credentials = load_credentials(tm_config.host)
114
122
 
115
- protocol = credentials[:transport_protocol] || tm_config.protocol
123
+ protocol = credentials&.dig(:transport_protocol) || tm_config.protocol
116
124
  train_config = tm_config.to_hash.select { |k| Train.options(protocol).key?(k) }
117
125
  logger.trace("Using target mode options from #{ChefUtils::Dist::Infra::PRODUCT} config file: #{train_config.keys.join(", ")}") if train_config
118
126
 
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Bryan McLellan <btm@loftninjas.org>
3
- # Copyright:: Copyright (c) Chef Software Inc.
3
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -57,8 +57,8 @@ module ChefConfig
57
57
 
58
58
  def self.join(*args, windows: ChefUtils.windows?)
59
59
  path_separator_regex = Regexp.escape(windows ? "#{File::SEPARATOR}#{BACKSLASH}" : File::SEPARATOR)
60
- trailing_slashes_regex = /[#{path_separator_regex}]+$/.freeze
61
- leading_slashes_regex = /^[#{path_separator_regex}]+/.freeze
60
+ trailing_slashes_regex = /[#{path_separator_regex}]+$/
61
+ leading_slashes_regex = /^[#{path_separator_regex}]+/
62
62
  separator = path_separator(windows: windows)
63
63
 
64
64
  args.flatten!
@@ -152,11 +152,12 @@ module ChefConfig
152
152
  path = Pathname.new(path).cleanpath.to_s
153
153
  if windows
154
154
  # ensure all forward slashes are backslashes
155
- path.gsub(File::SEPARATOR, path_separator(windows: windows))
155
+ path.gsub!(File::SEPARATOR, path_separator(windows: windows))
156
156
  else
157
157
  # ensure all backslashes are forward slashes
158
- path.gsub(BACKSLASH, File::SEPARATOR)
158
+ path.gsub!(BACKSLASH, File::SEPARATOR)
159
159
  end
160
+ path
160
161
  end
161
162
 
162
163
  # This is not just escaping for something like use in Regexps, or in globs. For the former
@@ -1,4 +1,4 @@
1
- # Copyright:: Copyright (c) Chef Software Inc.
1
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
2
2
  # License:: Apache License, Version 2.0
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,5 +15,5 @@
15
15
 
16
16
  module ChefConfig
17
17
  CHEFCONFIG_ROOT = File.expand_path("..", __dir__)
18
- VERSION = "18.10.17".freeze
18
+ VERSION = "19.1.164".freeze
19
19
  end
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Daniel DeLeo (<dan@chef.io>)
3
- # Copyright:: Copyright (c) Chef Software Inc.
3
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
data/lib/chef-config.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright:: Copyright (c) Chef Software Inc.
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
3
  # License:: Apache License, Version 2.0
4
4
  #
5
5
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,7 +1,7 @@
1
1
  #
2
2
  # Author:: Adam Jacob (<adam@chef.io>)
3
3
  # Author:: Kyle Goodwin (<kgoodwin@primerevenue.com>)
4
- # Copyright:: Copyright (c) Chef Software Inc.
4
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,6 +22,8 @@ require "chef-config/config"
22
22
  require "date" unless defined?(Date)
23
23
 
24
24
  RSpec.describe ChefConfig::Config do
25
+ let(:target_mode_host) { "fluffy.kittens.org".freeze }
26
+
25
27
  before(:each) do
26
28
  ChefConfig::Config.reset
27
29
 
@@ -379,8 +381,6 @@ RSpec.describe ChefConfig::Config do
379
381
  end
380
382
 
381
383
  context "when target mode is enabled" do
382
- let(:target_mode_host) { "fluffy.kittens.org" }
383
-
384
384
  before do
385
385
  ChefConfig::Config.target_mode.enabled = true
386
386
  ChefConfig::Config.target_mode.host = target_mode_host
@@ -400,6 +400,25 @@ RSpec.describe ChefConfig::Config do
400
400
  end
401
401
  end
402
402
 
403
+ describe "ChefConfig::Config[:chef_guid_path]" do
404
+ it "sets the default path to the chef guid" do
405
+ expected_path = ChefConfig::PathHelper.join(ChefConfig::Config.config_dir, "chef_guid")
406
+ expect(ChefConfig::Config.chef_guid_path).to eq(expected_path)
407
+ end
408
+
409
+ context "when target mode is enabled" do
410
+ before do
411
+ ChefConfig::Config.target_mode.enabled = true
412
+ ChefConfig::Config.target_mode.host = target_mode_host
413
+ end
414
+
415
+ it "sets the default path to the chef guid with the target host name" do
416
+ expected_path = ChefConfig::PathHelper.join(ChefConfig::Config.config_dir, target_mode_host, "chef_guid")
417
+ expect(ChefConfig::Config.chef_guid_path).to eq(expected_path)
418
+ end
419
+ end
420
+ end
421
+
403
422
  describe "ChefConfig::Config[:fips]" do
404
423
  let(:fips_enabled) { false }
405
424
 
@@ -495,7 +514,6 @@ RSpec.describe ChefConfig::Config do
495
514
  end
496
515
 
497
516
  describe "ChefConfig::Config[:cache_path]" do
498
- let(:target_mode_host) { "fluffy.kittens.org" }
499
517
  let(:target_mode_primary_cache_path) { ChefUtils.windows? ? "#{primary_cache_path}\\#{target_mode_host}" : "#{primary_cache_path}/#{target_mode_host}" }
500
518
  let(:target_mode_secondary_cache_path) { ChefUtils.windows? ? "#{secondary_cache_path}\\#{target_mode_host}" : "#{secondary_cache_path}/#{target_mode_host}" }
501
519
 
@@ -0,0 +1,181 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "spec_helper"
19
+ require "chef-config/config"
20
+ require "chef-config/mixin/credentials"
21
+
22
+ module Vault
23
+ class Client; end
24
+ end
25
+
26
+ RSpec.describe ChefConfig::Mixin::Credentials do
27
+
28
+ let(:test_class) { Class.new { include ChefConfig::Mixin::Credentials } }
29
+ subject(:test_obj) { test_class.new }
30
+
31
+ describe "#credentials_profile" do
32
+ context "when an explicit profile is given" do
33
+ it "passes it through" do
34
+ expect(test_obj.credentials_profile("webserver")).to eq("webserver")
35
+ end
36
+ end
37
+
38
+ context "when an environment variable is set" do
39
+ before(:all) do
40
+ @original_env = ENV.to_hash
41
+ end
42
+
43
+ after(:all) do
44
+ ENV.clear
45
+ ENV.update(@original_env)
46
+ end
47
+
48
+ before(:each) do
49
+ ENV["CHEF_PROFILE"] = "acme-server"
50
+ end
51
+
52
+ it "picks the profile correctly" do
53
+ expect(test_obj.credentials_profile).to eq("acme-server")
54
+ end
55
+ end
56
+
57
+ context "when no profile is given" do
58
+ it "picks the default one" do
59
+ expect(test_obj.credentials_profile).to eq("default")
60
+ end
61
+ end
62
+ end
63
+
64
+ describe "#resolve_secrets" do
65
+ context "when no credentials were loaded" do
66
+ it "returns" do
67
+ allow(test_obj).to receive(:credentials_config).and_return(nil)
68
+ expect(test_obj.resolve_secrets("dummy")).to eq(nil)
69
+ end
70
+ end
71
+
72
+ context "when credentials do not contain specified profile" do
73
+ it "raises an error" do
74
+ allow(test_obj).to receive(:credentials_config).and_return({ "webserver" => {} })
75
+ expect { test_obj.resolve_secrets("dummy") }.to raise_error(ChefConfig::NoCredentialsFound, /No credentials found for profile/)
76
+ end
77
+ end
78
+
79
+ context "when no secrets were referenced in profile" do
80
+ it "returns" do
81
+ allow(test_obj).to receive(:credentials_config).and_return({ "webserver2" => {} })
82
+ expect(test_obj.resolve_secrets("webserver2")).to eq(nil)
83
+ end
84
+ end
85
+ end
86
+
87
+ describe "#valid_secrets_provider?" do
88
+ context "when global, valid configuration was provided" do
89
+ let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-vault", "endpoint" => "https://198.51.100.5:8200", "token" => "hvs.1234567890" } } }
90
+ let(:secrets_config) { { "secret" => "/chef/sudo_password", "field" => "password" } }
91
+
92
+ it "returns true" do
93
+ allow(test_obj).to receive(:global_options).and_return(global_options)
94
+ expect(test_obj.valid_secrets_provider?(secrets_config)).to be(true)
95
+ end
96
+ end
97
+
98
+ context "when global, invalid configuration was provided" do
99
+ let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-consul" } } }
100
+ let(:secrets_config) { { "secret" => "/chef/sudo_password", "field" => "password" } }
101
+
102
+ it "returns false" do
103
+ allow(test_obj).to receive(:global_options).and_return(global_options)
104
+ expect(test_obj.valid_secrets_provider?(secrets_config)).to be(false)
105
+ end
106
+ end
107
+
108
+ context "when global, invalid configuration is overridden with a correct value" do
109
+ let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-consul" } } }
110
+ let(:secrets_config) { { "secrets_provider" => { "name" => "hashicorp-vault" }, "secret" => "/chef/sudo_password", "field" => "password" } }
111
+
112
+ it "returns false" do
113
+ allow(test_obj).to receive(:global_options).and_return(global_options)
114
+ expect(test_obj.valid_secrets_provider?(secrets_config)).to be(true)
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "#resolve_secret" do
120
+ before do
121
+ allow(test_obj).to receive(:global_options).and_return(global_options)
122
+
123
+ # Simulate "vault" gem
124
+ allow(test_obj).to receive(:require).with("vault")
125
+ vault_double = double("Vault::Client")
126
+ allow(vault_double).to receive_message_chain("logical.read") { secrets_result }
127
+ allow(vault_double).to receive_message_chain("kv.read.data") { secrets_result }
128
+ test_obj.instance_variable_set(:@vault, vault_double)
129
+
130
+ # Simulate "jmespath" gem
131
+ allow(test_obj).to receive(:require).with("jmespath")
132
+ jmespath_double = double("JMESPath")
133
+ allow(jmespath_double).to receive_message_chain("search") { search_for = secrets_config["field"]; secrets_result[search_for] }
134
+ stub_const("::JMESPath", jmespath_double)
135
+ end
136
+
137
+ context "without default secrets provider being set" do
138
+ let(:global_options) {}
139
+
140
+ context "for a secret of type string" do
141
+ let(:secrets_result) { "secret" }
142
+ let(:secrets_config) { { "secrets_provider" => { "name" => "hashicorp-vault" }, "secret" => "/chef/sudo_password" } }
143
+
144
+ it "returns the complete value" do
145
+ expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
146
+ end
147
+ end
148
+
149
+ context "for a secret of type hash" do
150
+ let(:secrets_result) { { "password" => "secret" } }
151
+ let(:secrets_config) { { "secrets_provider" => { "name" => "hashicorp-vault", "endpoint" => "https://198.51.100.5:8200", "token" => "hvs.1234567890" }, "secret" => "/chef/sudo_password", "field" => "password" } }
152
+
153
+ it "returns the correct subkey" do
154
+ expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
155
+ end
156
+ end
157
+ end
158
+
159
+ context "with default secrets provider being set" do
160
+ let(:global_options) { { "default_secrets_provider" => { "name" => "hashicorp-vault", "endpoint" => "https://198.51.100.5:8200", "token" => "hvs.1234567890" } } }
161
+
162
+ context "for a secret of type string" do
163
+ let(:secrets_result) { "secret" }
164
+ let(:secrets_config) { { "secret" => "/chef/sudo_password" } }
165
+
166
+ it "returns the complete value" do
167
+ expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
168
+ end
169
+ end
170
+
171
+ context "for a secret of type hash" do
172
+ let(:secrets_result) { { "password" => "secret" } }
173
+ let(:secrets_config) { { "secret" => "/chef/sudo_password", "field" => "password" } }
174
+
175
+ it "returns the correct subkey" do
176
+ expect(test_obj.resolve_secret(secrets_config)).to eq("secret")
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Matt Wrock (<matt@mattwrock.com>)
3
- # Copyright:: Copyright (c) Chef Software Inc.
3
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Bryan McLellan <btm@loftninjas.org>
3
- # Copyright:: Copyright (c) Chef Software Inc.
3
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Daniel DeLeo (<dan@chef.io>)
3
- # Copyright:: Copyright (c) Chef Software Inc.
3
+ # Copyright:: Copyright (c) 2009-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
4
4
  # License:: Apache License, Version 2.0
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-config
3
3
  version: !ruby/object:Gem::Version
4
- version: 18.10.17
4
+ version: 19.1.164
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Jacob
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-02-25 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: chef-utils
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - '='
18
17
  - !ruby/object:Gem::Version
19
- version: 18.10.17
18
+ version: 19.1.164
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - '='
25
24
  - !ruby/object:Gem::Version
26
- version: 18.10.17
25
+ version: 19.1.164
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: mixlib-shellout
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -106,7 +105,20 @@ dependencies:
106
105
  - - "~>"
107
106
  - !ruby/object:Gem::Version
108
107
  version: '1.2'
109
- description:
108
+ - !ruby/object:Gem::Dependency
109
+ name: racc
110
+ requirement: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ type: :runtime
116
+ prerelease: false
117
+ version_requirements: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
110
122
  email:
111
123
  - adam@chef.io
112
124
  executables: []
@@ -131,6 +143,7 @@ files:
131
143
  - lib/chef-config/workstation_config_loader.rb
132
144
  - spec/spec_helper.rb
133
145
  - spec/unit/config_spec.rb
146
+ - spec/unit/credentials_spec.rb
134
147
  - spec/unit/fips_spec.rb
135
148
  - spec/unit/path_helper_spec.rb
136
149
  - spec/unit/workstation_config_loader_spec.rb
@@ -143,7 +156,6 @@ metadata:
143
156
  documentation_uri: https://github.com/chef/chef/tree/main/chef-config/README.md
144
157
  homepage_uri: https://github.com/chef/chef/tree/main/chef-config
145
158
  source_code_uri: https://github.com/chef/chef/tree/main/chef-config
146
- post_install_message:
147
159
  rdoc_options: []
148
160
  require_paths:
149
161
  - lib
@@ -158,8 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
170
  - !ruby/object:Gem::Version
159
171
  version: '0'
160
172
  requirements: []
161
- rubygems_version: 3.3.27
162
- signing_key:
173
+ rubygems_version: 3.6.9
163
174
  specification_version: 4
164
175
  summary: Chef Infra's default configuration and config loading library
165
176
  test_files: []