## Author:: Daniel DeLeo (<dan@chef.io>)# Copyright:: Copyright 2014-2016, Chef Software, Inc.# License:: Apache License, Version 2.0## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.#require"chef-config/config"require"chef-config/exceptions"require"chef-config/logger"require"chef-config/path_helper"require"chef-config/windows"require"chef-config/mixin/dot_d"require"chef-config/mixin/credentials"moduleChefConfigclassWorkstationConfigLoaderincludeChefConfig::Mixin::DotDincludeChefConfig::Mixin::Credentials# Path to a config file requested by user, (e.g., via command line option). Can be nilattr_accessor:explicit_config_file# The name of a credentials profile. Can be nilattr_accessor:profileattr_reader:credentials_found# TODO: initialize this with a logger for Chef and Knifedefinitialize(explicit_config_file,logger=nil,profile: nil)@explicit_config_file=explicit_config_file@chef_config_dir=nil@config_location=nil@profile=profile@logger=logger||NullLogger.new@credentials_found=falseenddefno_config_found?config_location.nil?&&!credentials_foundenddefconfig_location@config_location||=(explicit_config_file||locate_local_config)enddefchef_config_dirif@chef_config_dir.nil?@chef_config_dir=falsefull_path=working_directory.split(File::SEPARATOR)(full_path.length-1).downto(0)do|i|candidate_directory=File.join(full_path[0..i]+[".chef"])ifFile.exist?(candidate_directory)&&File.directory?(candidate_directory)@chef_config_dir=candidate_directorybreakendendend@chef_config_direnddefloadload_credentials(profile)# Ignore it if there's no explicit_config_file and can't find one at a# default path.if!config_location.nil?ifexplicit_config_file&&!path_exists?(config_location)raiseChefConfig::ConfigurationError,"Specified config file #{config_location} does not exist"end# Have to set Config.config_file b/c other config is derived from it.Config.config_file=config_locationapply_config(IO.read(config_location),config_location)endload_dot_d(Config[:config_d_dir])ifConfig[:config_d_dir]end# (Private API, public for test purposes)defenvENVend# (Private API, public for test purposes)defpath_exists?(path)Pathname.new(path).expand_path.exist?endprivatedefhave_config?(path)ifpath_exists?(path)logger.info("Using config at #{path}")trueelselogger.debug("Config not found at #{path}, trying next option")falseendenddeflocate_local_configcandidate_configs=[]# Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine)ifenv["KNIFE_HOME"]candidate_configs<<File.join(env["KNIFE_HOME"],"config.rb")candidate_configs<<File.join(env["KNIFE_HOME"],"knife.rb")end# Look for $PWD/knife.rbifDir.pwdcandidate_configs<<File.join(Dir.pwd,"config.rb")candidate_configs<<File.join(Dir.pwd,"knife.rb")end# Look for $UPWARD/.chef/knife.rbifchef_config_dircandidate_configs<<File.join(chef_config_dir,"config.rb")candidate_configs<<File.join(chef_config_dir,"knife.rb")end# Look for $HOME/.chef/knife.rbPathHelper.home(".chef")do|dot_chef_dir|candidate_configs<<File.join(dot_chef_dir,"config.rb")candidate_configs<<File.join(dot_chef_dir,"knife.rb")endcandidate_configs.finddo|candidate_config|have_config?(candidate_config)endenddefworking_directorya=ifChefConfig.windows?env["CD"]elseenv["PWD"]end||Dir.pwdaenddefapply_credentials(creds,profile)Config.profile||=profileifcreds.key?("node_name")&&creds.key?("client_name")raiseChefConfig::ConfigurationError,"Do not specify both node_name and client_name. You should prefer client_name."endConfig.node_name=creds.fetch("node_name")ifcreds.key?("node_name")Config.node_name=creds.fetch("client_name")ifcreds.key?("client_name")Config.chef_server_url=creds.fetch("chef_server_url")ifcreds.key?("chef_server_url")Config.validation_client_name=creds.fetch("validation_client_name")ifcreds.key?("validation_client_name")extract_key(creds,"validation_key",:validation_key,:validation_key_contents)extract_key(creds,"validator_key",:validation_key,:validation_key_contents)extract_key(creds,"client_key",:client_key,:client_key_contents)@credentials_found=trueenddefextract_key(creds,name,config_path,config_contents)returnunlesscreds.has_key?(name)val=creds.fetch(name)ifval.start_with?("-----BEGIN RSA PRIVATE KEY-----")Config.send(config_contents,val)elseabs_path=Pathname.new(val).expand_path(home_chef_dir)Config.send(config_path,abs_path)endenddefhome_chef_dir@home_chef_dir||=PathHelper.home(".chef")enddefapply_config(config_content,config_file_path)Config.from_string(config_content,config_file_path)rescueSignalExceptionraiserescueSyntaxError=>emessage=""message<<"You have invalid ruby syntax in your config file #{config_file_path}\n\n"message<<"#{e.class.name}: #{e.message}\n"iffile_line=e.message[/#{Regexp.escape(config_file_path)}:[\d]+/]line=file_line[/:([\d]+)$/,1].to_imessage<<highlight_config_error(config_file_path,line)endraiseChefConfig::ConfigurationError,messagerescueException=>emessage="You have an error in your config file #{config_file_path}\n\n"message<<"#{e.class.name}: #{e.message}\n"filtered_trace=e.backtrace.grep(/#{Regexp.escape(config_file_path)}/)filtered_trace.each{|bt_line|message<<" "<<bt_line<<"\n"}if!filtered_trace.empty?line_nr=filtered_trace.first[/#{Regexp.escape(config_file_path)}:([\d]+)/,1]message<<highlight_config_error(config_file_path,line_nr.to_i)endraiseChefConfig::ConfigurationError,messageenddefhighlight_config_error(file,line)config_file_lines=[]IO.readlines(file).each_with_index{|l,i|config_file_lines<<"#{(i+1).to_s.rjust(3)}: #{l.chomp}"}ifline==1lines=config_file_lines[0..3]elselines=config_file_lines[Range.new(line-2,line)]end"Relevant file content:\n"+lines.join("\n")+"\n"enddeflogger@loggerendendend