chef-config 18.1.29 → 18.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,19 +1,19 @@
1
- # Copyright:: Copyright (c) Chef Software Inc.
2
- # License:: Apache License, Version 2.0
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- module ChefConfig
17
- CHEFCONFIG_ROOT = File.expand_path("..", __dir__)
18
- VERSION = "18.1.29".freeze
19
- end
1
+ # Copyright:: Copyright (c) Chef Software Inc.
2
+ # License:: Apache License, Version 2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module ChefConfig
17
+ CHEFCONFIG_ROOT = File.expand_path("..", __dir__)
18
+ VERSION = "18.2.5".freeze
19
+ end
@@ -1,24 +1,24 @@
1
- #
2
- # Copyright:: Copyright (c) Chef Software Inc.
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 "chef-utils" unless defined?(ChefUtils::CANARY)
19
-
20
- module ChefConfig
21
- def self.windows?
22
- ChefUtils.windows?
23
- end
24
- end
1
+ #
2
+ # Copyright:: Copyright (c) Chef Software Inc.
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 "chef-utils" unless defined?(ChefUtils::CANARY)
19
+
20
+ module ChefConfig
21
+ def self.windows?
22
+ ChefUtils.windows?
23
+ end
24
+ end
@@ -1,282 +1,282 @@
1
- #
2
- # Author:: Daniel DeLeo (<dan@chef.io>)
3
- # Copyright:: Copyright (c) Chef Software Inc.
4
- # License:: Apache License, Version 2.0
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
- #
18
-
19
- require "chef-utils" unless defined?(ChefUtils::CANARY)
20
- require "etc" unless defined?(Etc)
21
- require_relative "config"
22
- require_relative "exceptions"
23
- require_relative "logger"
24
- require_relative "path_helper"
25
- require_relative "windows"
26
- require_relative "mixin/dot_d"
27
- require_relative "mixin/credentials"
28
-
29
- module ChefConfig
30
- class WorkstationConfigLoader
31
- include ChefConfig::Mixin::DotD
32
- include ChefConfig::Mixin::Credentials
33
-
34
- # Path to a config file requested by user, (e.g., via command line option). Can be nil
35
- attr_accessor :explicit_config_file
36
- # The name of a credentials profile. Can be nil
37
- attr_accessor :profile
38
- attr_reader :credentials_found
39
-
40
- # TODO: initialize this with a logger for Chef and Knife
41
- def initialize(explicit_config_file, logger = nil, profile: nil)
42
- @explicit_config_file = explicit_config_file
43
- @chef_config_dir = nil
44
- @config_location = nil
45
- @profile = profile
46
- @logger = logger || NullLogger.new
47
- @credentials_found = false
48
- end
49
-
50
- def no_config_found?
51
- config_location.nil? && !credentials_found
52
- end
53
-
54
- def config_location
55
- @config_location ||= (explicit_config_file || locate_local_config)
56
- end
57
-
58
- def chef_config_dir
59
- if @chef_config_dir.nil?
60
- @chef_config_dir = false
61
- full_path = working_directory.split(File::SEPARATOR)
62
- (full_path.length - 1).downto(0) do |i|
63
- candidate_directory = File.join(full_path[0..i] + [ChefUtils::Dist::Infra::USER_CONF_DIR])
64
- if File.exist?(candidate_directory) && File.directory?(candidate_directory)
65
- @chef_config_dir = candidate_directory
66
- break
67
- end
68
- end
69
- end
70
- @chef_config_dir
71
- end
72
-
73
- def load
74
- load_credentials(profile)
75
- # Ignore it if there's no explicit_config_file and can't find one at a
76
- # default path.
77
- unless config_location.nil?
78
- if explicit_config_file && !path_exists?(config_location)
79
- raise ChefConfig::ConfigurationError, "Specified config file #{config_location} does not exist"
80
- end
81
-
82
- # Have to set Config.config_file b/c other config is derived from it.
83
- Config.config_file = config_location
84
- apply_config(IO.read(config_location), config_location)
85
- end
86
-
87
- load_dot_d(Config[:config_d_dir]) if Config[:config_d_dir]
88
-
89
- apply_defaults
90
- end
91
-
92
- # (Private API, public for test purposes)
93
- def env
94
- ENV
95
- end
96
-
97
- # (Private API, public for test purposes)
98
- def path_exists?(path)
99
- Pathname.new(path).expand_path.exist?
100
- end
101
-
102
- private
103
-
104
- def have_config?(path)
105
- if path_exists?(path)
106
- logger.info("Using config at #{path}")
107
- true
108
- else
109
- logger.debug("Config not found at #{path}, trying next option")
110
- false
111
- end
112
- end
113
-
114
- def locate_local_config
115
- candidate_configs = []
116
-
117
- # Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine)
118
- if env["KNIFE_HOME"]
119
- candidate_configs << File.join(env["KNIFE_HOME"], "config.rb")
120
- candidate_configs << File.join(env["KNIFE_HOME"], "knife.rb")
121
- end
122
- # Look for $PWD/knife.rb
123
- if Dir.pwd
124
- candidate_configs << File.join(Dir.pwd, "config.rb")
125
- candidate_configs << File.join(Dir.pwd, "knife.rb")
126
- end
127
- # Look for $UPWARD/.chef/knife.rb
128
- if chef_config_dir
129
- candidate_configs << File.join(chef_config_dir, "config.rb")
130
- candidate_configs << File.join(chef_config_dir, "knife.rb")
131
- end
132
- # Look for $HOME/.chef/knife.rb
133
- PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR) do |dot_chef_dir|
134
- candidate_configs << File.join(dot_chef_dir, "config.rb")
135
- candidate_configs << File.join(dot_chef_dir, "knife.rb")
136
- end
137
-
138
- candidate_configs.find do |candidate_config|
139
- have_config?(candidate_config)
140
- end
141
- end
142
-
143
- def working_directory
144
- if ChefUtils.windows?
145
- env["CD"]
146
- else
147
- env["PWD"]
148
- end || Dir.pwd
149
- end
150
-
151
- def apply_credentials(creds, profile)
152
- # Store the profile used in case other things want it.
153
- Config.profile ||= profile
154
- # Validate the credentials data.
155
- if creds.key?("node_name") && creds.key?("client_name")
156
- raise ChefConfig::ConfigurationError, "Do not specify both node_name and client_name. You should prefer client_name."
157
- end
158
-
159
- # Load credentials data into the Chef configuration.
160
- creds.each do |key, value|
161
- case key.to_s
162
- when "client_name"
163
- # Special case because it's weird to set your username via `node_name`.
164
- Config.node_name = value
165
- when "validation_key", "validator_key"
166
- extract_key(value, :validation_key, :validation_key_contents)
167
- when "client_key"
168
- extract_key(value, :client_key, :client_key_contents)
169
- when "knife"
170
- Config.knife.merge!(value.transform_keys(&:to_sym))
171
- else
172
- Config[key.to_sym] = value
173
- end
174
- end
175
- @credentials_found = true
176
- end
177
-
178
- def extract_key(key_value, config_path, config_contents)
179
- if key_value.start_with?("-----BEGIN RSA PRIVATE KEY-----")
180
- Config.send(config_contents, key_value)
181
- else
182
- abs_path = Pathname.new(key_value).expand_path(home_chef_dir)
183
- Config.send(config_path, abs_path)
184
- end
185
- end
186
-
187
- def home_chef_dir
188
- @home_chef_dir ||= PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR)
189
- end
190
-
191
- def apply_config(config_content, config_file_path)
192
- Config.from_string(config_content, config_file_path)
193
- rescue SignalException
194
- raise
195
- rescue SyntaxError => e
196
- message = ""
197
- message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n"
198
- message << "#{e.class.name}: #{e.message}\n"
199
- if file_line = e.message[/#{Regexp.escape(config_file_path)}:\d+/]
200
- line = file_line[/:(\d+)$/, 1].to_i
201
- message << highlight_config_error(config_file_path, line)
202
- end
203
- raise ChefConfig::ConfigurationError, message
204
- rescue Exception => e
205
- message = "You have an error in your config file #{config_file_path}\n\n"
206
- message << "#{e.class.name}: #{e.message}\n"
207
- filtered_trace = e.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
208
- filtered_trace.each { |bt_line| message << " " << bt_line << "\n" }
209
- unless filtered_trace.empty?
210
- line_nr = filtered_trace.first[/#{Regexp.escape(config_file_path)}:(\d+)/, 1]
211
- message << highlight_config_error(config_file_path, line_nr.to_i)
212
- end
213
- raise ChefConfig::ConfigurationError, message
214
- end
215
-
216
- # Apply default configuration values for workstation-style tools.
217
- #
218
- # Global defaults should go in {ChefConfig::Config} instead, this is only
219
- # for things like `knife` and `chef`.
220
- #
221
- # @api private
222
- # @since 14.3
223
- # @return [void]
224
- def apply_defaults
225
- # If we don't have a better guess use the username.
226
- Config[:node_name] ||= Etc.getlogin
227
- # If we don't have a key (path or inline) check user.pem and $node_name.pem.
228
- unless Config.key?(:client_key) || Config.key?(:client_key_contents)
229
- key_path = find_default_key(["#{Config[:node_name]}.pem", "user.pem"])
230
- Config[:client_key] = key_path if key_path
231
- end
232
- # Similarly look for a validation key file, though this should be less
233
- # common these days.
234
- unless Config.key?(:validation_key) || Config.key?(:validation_key_contents)
235
- key_path = find_default_key(["#{Config[:validation_client_name]}.pem", "validator.pem", "validation.pem"])
236
- Config[:validation_key] = key_path if key_path
237
- end
238
- end
239
-
240
- # Look for a default key file.
241
- #
242
- # This searches for any of a list of possible default keys, checking both
243
- # the local `.chef/` folder and the home directory `~/.chef/`. Returns `nil`
244
- # if no matching file is found.
245
- #
246
- # @api private
247
- # @since 14.3
248
- # @param key_names [Array<String>] A list of possible filenames to check for.
249
- # The first one found will be returned.
250
- # @return [String, nil]
251
- def find_default_key(key_names)
252
- key_names.each do |filename|
253
- path = Pathname.new(filename)
254
- # If we have a config location (like ./.chef/), look there first.
255
- if config_location
256
- local_path = path.expand_path(File.dirname(config_location))
257
- return local_path.to_s if local_path.exist?
258
- end
259
- # Then check ~/.chef.
260
- home_path = path.expand_path(home_chef_dir)
261
- return home_path.to_s if home_path.exist?
262
- end
263
- nil
264
- end
265
-
266
- def highlight_config_error(file, line)
267
- config_file_lines = []
268
- IO.readlines(file).each_with_index { |l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}" }
269
- if line == 1
270
- lines = config_file_lines[0..3]
271
- else
272
- lines = config_file_lines[Range.new(line - 2, line)]
273
- end
274
- "Relevant file content:\n" + lines.join("\n") + "\n"
275
- end
276
-
277
- def logger
278
- @logger
279
- end
280
-
281
- end
282
- end
1
+ #
2
+ # Author:: Daniel DeLeo (<dan@chef.io>)
3
+ # Copyright:: Copyright (c) Chef Software Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "chef-utils" unless defined?(ChefUtils::CANARY)
20
+ require "etc" unless defined?(Etc)
21
+ require_relative "config"
22
+ require_relative "exceptions"
23
+ require_relative "logger"
24
+ require_relative "path_helper"
25
+ require_relative "windows"
26
+ require_relative "mixin/dot_d"
27
+ require_relative "mixin/credentials"
28
+
29
+ module ChefConfig
30
+ class WorkstationConfigLoader
31
+ include ChefConfig::Mixin::DotD
32
+ include ChefConfig::Mixin::Credentials
33
+
34
+ # Path to a config file requested by user, (e.g., via command line option). Can be nil
35
+ attr_accessor :explicit_config_file
36
+ # The name of a credentials profile. Can be nil
37
+ attr_accessor :profile
38
+ attr_reader :credentials_found
39
+
40
+ # TODO: initialize this with a logger for Chef and Knife
41
+ def initialize(explicit_config_file, logger = nil, profile: nil)
42
+ @explicit_config_file = explicit_config_file
43
+ @chef_config_dir = nil
44
+ @config_location = nil
45
+ @profile = profile
46
+ @logger = logger || NullLogger.new
47
+ @credentials_found = false
48
+ end
49
+
50
+ def no_config_found?
51
+ config_location.nil? && !credentials_found
52
+ end
53
+
54
+ def config_location
55
+ @config_location ||= (explicit_config_file || locate_local_config)
56
+ end
57
+
58
+ def chef_config_dir
59
+ if @chef_config_dir.nil?
60
+ @chef_config_dir = false
61
+ full_path = working_directory.split(File::SEPARATOR)
62
+ (full_path.length - 1).downto(0) do |i|
63
+ candidate_directory = File.join(full_path[0..i] + [ChefUtils::Dist::Infra::USER_CONF_DIR])
64
+ if File.exist?(candidate_directory) && File.directory?(candidate_directory)
65
+ @chef_config_dir = candidate_directory
66
+ break
67
+ end
68
+ end
69
+ end
70
+ @chef_config_dir
71
+ end
72
+
73
+ def load
74
+ load_credentials(profile)
75
+ # Ignore it if there's no explicit_config_file and can't find one at a
76
+ # default path.
77
+ unless config_location.nil?
78
+ if explicit_config_file && !path_exists?(config_location)
79
+ raise ChefConfig::ConfigurationError, "Specified config file #{config_location} does not exist"
80
+ end
81
+
82
+ # Have to set Config.config_file b/c other config is derived from it.
83
+ Config.config_file = config_location
84
+ apply_config(IO.read(config_location), config_location)
85
+ end
86
+
87
+ load_dot_d(Config[:config_d_dir]) if Config[:config_d_dir]
88
+
89
+ apply_defaults
90
+ end
91
+
92
+ # (Private API, public for test purposes)
93
+ def env
94
+ ENV
95
+ end
96
+
97
+ # (Private API, public for test purposes)
98
+ def path_exists?(path)
99
+ Pathname.new(path).expand_path.exist?
100
+ end
101
+
102
+ private
103
+
104
+ def have_config?(path)
105
+ if path_exists?(path)
106
+ logger.info("Using config at #{path}")
107
+ true
108
+ else
109
+ logger.debug("Config not found at #{path}, trying next option")
110
+ false
111
+ end
112
+ end
113
+
114
+ def locate_local_config
115
+ candidate_configs = []
116
+
117
+ # Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine)
118
+ if env["KNIFE_HOME"]
119
+ candidate_configs << File.join(env["KNIFE_HOME"], "config.rb")
120
+ candidate_configs << File.join(env["KNIFE_HOME"], "knife.rb")
121
+ end
122
+ # Look for $PWD/knife.rb
123
+ if Dir.pwd
124
+ candidate_configs << File.join(Dir.pwd, "config.rb")
125
+ candidate_configs << File.join(Dir.pwd, "knife.rb")
126
+ end
127
+ # Look for $UPWARD/.chef/knife.rb
128
+ if chef_config_dir
129
+ candidate_configs << File.join(chef_config_dir, "config.rb")
130
+ candidate_configs << File.join(chef_config_dir, "knife.rb")
131
+ end
132
+ # Look for $HOME/.chef/knife.rb
133
+ PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR) do |dot_chef_dir|
134
+ candidate_configs << File.join(dot_chef_dir, "config.rb")
135
+ candidate_configs << File.join(dot_chef_dir, "knife.rb")
136
+ end
137
+
138
+ candidate_configs.find do |candidate_config|
139
+ have_config?(candidate_config)
140
+ end
141
+ end
142
+
143
+ def working_directory
144
+ if ChefUtils.windows?
145
+ env["CD"]
146
+ else
147
+ env["PWD"]
148
+ end || Dir.pwd
149
+ end
150
+
151
+ def apply_credentials(creds, profile)
152
+ # Store the profile used in case other things want it.
153
+ Config.profile ||= profile
154
+ # Validate the credentials data.
155
+ if creds.key?("node_name") && creds.key?("client_name")
156
+ raise ChefConfig::ConfigurationError, "Do not specify both node_name and client_name. You should prefer client_name."
157
+ end
158
+
159
+ # Load credentials data into the Chef configuration.
160
+ creds.each do |key, value|
161
+ case key.to_s
162
+ when "client_name"
163
+ # Special case because it's weird to set your username via `node_name`.
164
+ Config.node_name = value
165
+ when "validation_key", "validator_key"
166
+ extract_key(value, :validation_key, :validation_key_contents)
167
+ when "client_key"
168
+ extract_key(value, :client_key, :client_key_contents)
169
+ when "knife"
170
+ Config.knife.merge!(value.transform_keys(&:to_sym))
171
+ else
172
+ Config[key.to_sym] = value
173
+ end
174
+ end
175
+ @credentials_found = true
176
+ end
177
+
178
+ def extract_key(key_value, config_path, config_contents)
179
+ if key_value.start_with?("-----BEGIN RSA PRIVATE KEY-----")
180
+ Config.send(config_contents, key_value)
181
+ else
182
+ abs_path = Pathname.new(key_value).expand_path(home_chef_dir)
183
+ Config.send(config_path, abs_path)
184
+ end
185
+ end
186
+
187
+ def home_chef_dir
188
+ @home_chef_dir ||= PathHelper.home(ChefUtils::Dist::Infra::USER_CONF_DIR)
189
+ end
190
+
191
+ def apply_config(config_content, config_file_path)
192
+ Config.from_string(config_content, config_file_path)
193
+ rescue SignalException
194
+ raise
195
+ rescue SyntaxError => e
196
+ message = ""
197
+ message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n"
198
+ message << "#{e.class.name}: #{e.message}\n"
199
+ if file_line = e.message[/#{Regexp.escape(config_file_path)}:\d+/]
200
+ line = file_line[/:(\d+)$/, 1].to_i
201
+ message << highlight_config_error(config_file_path, line)
202
+ end
203
+ raise ChefConfig::ConfigurationError, message
204
+ rescue Exception => e
205
+ message = "You have an error in your config file #{config_file_path}\n\n"
206
+ message << "#{e.class.name}: #{e.message}\n"
207
+ filtered_trace = e.backtrace.grep(/#{Regexp.escape(config_file_path)}/)
208
+ filtered_trace.each { |bt_line| message << " " << bt_line << "\n" }
209
+ unless filtered_trace.empty?
210
+ line_nr = filtered_trace.first[/#{Regexp.escape(config_file_path)}:(\d+)/, 1]
211
+ message << highlight_config_error(config_file_path, line_nr.to_i)
212
+ end
213
+ raise ChefConfig::ConfigurationError, message
214
+ end
215
+
216
+ # Apply default configuration values for workstation-style tools.
217
+ #
218
+ # Global defaults should go in {ChefConfig::Config} instead, this is only
219
+ # for things like `knife` and `chef`.
220
+ #
221
+ # @api private
222
+ # @since 14.3
223
+ # @return [void]
224
+ def apply_defaults
225
+ # If we don't have a better guess use the username.
226
+ Config[:node_name] ||= Etc.getlogin
227
+ # If we don't have a key (path or inline) check user.pem and $node_name.pem.
228
+ unless Config.key?(:client_key) || Config.key?(:client_key_contents)
229
+ key_path = find_default_key(["#{Config[:node_name]}.pem", "user.pem"])
230
+ Config[:client_key] = key_path if key_path
231
+ end
232
+ # Similarly look for a validation key file, though this should be less
233
+ # common these days.
234
+ unless Config.key?(:validation_key) || Config.key?(:validation_key_contents)
235
+ key_path = find_default_key(["#{Config[:validation_client_name]}.pem", "validator.pem", "validation.pem"])
236
+ Config[:validation_key] = key_path if key_path
237
+ end
238
+ end
239
+
240
+ # Look for a default key file.
241
+ #
242
+ # This searches for any of a list of possible default keys, checking both
243
+ # the local `.chef/` folder and the home directory `~/.chef/`. Returns `nil`
244
+ # if no matching file is found.
245
+ #
246
+ # @api private
247
+ # @since 14.3
248
+ # @param key_names [Array<String>] A list of possible filenames to check for.
249
+ # The first one found will be returned.
250
+ # @return [String, nil]
251
+ def find_default_key(key_names)
252
+ key_names.each do |filename|
253
+ path = Pathname.new(filename)
254
+ # If we have a config location (like ./.chef/), look there first.
255
+ if config_location
256
+ local_path = path.expand_path(File.dirname(config_location))
257
+ return local_path.to_s if local_path.exist?
258
+ end
259
+ # Then check ~/.chef.
260
+ home_path = path.expand_path(home_chef_dir)
261
+ return home_path.to_s if home_path.exist?
262
+ end
263
+ nil
264
+ end
265
+
266
+ def highlight_config_error(file, line)
267
+ config_file_lines = []
268
+ IO.readlines(file).each_with_index { |l, i| config_file_lines << "#{(i + 1).to_s.rjust(3)}: #{l.chomp}" }
269
+ if line == 1
270
+ lines = config_file_lines[0..3]
271
+ else
272
+ lines = config_file_lines[Range.new(line - 2, line)]
273
+ end
274
+ "Relevant file content:\n" + lines.join("\n") + "\n"
275
+ end
276
+
277
+ def logger
278
+ @logger
279
+ end
280
+
281
+ end
282
+ end