diff options
author | Claire McQuin <claire@getchef.com> | 2015-06-29 12:15:53 -0700 |
---|---|---|
committer | Claire McQuin <claire@getchef.com> | 2015-06-29 15:52:17 -0700 |
commit | 6a4f414666b3688a3dfa94b131f83235f4d2fa64 (patch) | |
tree | 97aabc62fb0cda8039f8bf44e6e5944e6d5430ed | |
parent | 1289161dd9b53a5e3c9f6c601f6552c452a3e5ad (diff) | |
download | chef-6a4f414666b3688a3dfa94b131f83235f4d2fa64.tar.gz |
Move workstation_config_loader into chef-configmcquin/chef-config/workstation-config-loader
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 3 | ||||
-rw-r--r-- | chef-config/lib/chef-config/exceptions.rb | 4 | ||||
-rw-r--r-- | chef-config/lib/chef-config/workstation_config_loader.rb | 179 | ||||
-rw-r--r-- | chef-config/spec/unit/workstation_config_loader_spec.rb (renamed from spec/unit/workstation_config_loader_spec.rb) | 25 | ||||
-rw-r--r-- | lib/chef/exceptions.rb | 5 | ||||
-rw-r--r-- | lib/chef/workstation_config_loader.rb | 161 |
6 files changed, 202 insertions, 175 deletions
diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index 63de8a451f..301a3ba0b6 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -739,6 +739,3 @@ module ChefConfig end end end - - - diff --git a/chef-config/lib/chef-config/exceptions.rb b/chef-config/lib/chef-config/exceptions.rb index f5d76d856b..1f80e505df 100644 --- a/chef-config/lib/chef-config/exceptions.rb +++ b/chef-config/lib/chef-config/exceptions.rb @@ -20,7 +20,7 @@ require 'chef-config/logger' module ChefConfig - class InvalidPath < StandardError - end + class ConfigurationError < ArgumentError; end + class InvalidPath < StandardError; end end diff --git a/chef-config/lib/chef-config/workstation_config_loader.rb b/chef-config/lib/chef-config/workstation_config_loader.rb new file mode 100644 index 0000000000..177cd776d4 --- /dev/null +++ b/chef-config/lib/chef-config/workstation_config_loader.rb @@ -0,0 +1,179 @@ +# +# Author:: Daniel DeLeo (<dan@chef.io>) +# Copyright:: Copyright (c) 2014 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' + +module ChefConfig + class WorkstationConfigLoader + + # Path to a config file requested by user, (e.g., via command line option). Can be nil + attr_accessor :explicit_config_file + + # TODO: initialize this with a logger for Chef and Knife + def initialize(explicit_config_file, logger=nil) + @explicit_config_file = explicit_config_file + @chef_config_dir = nil + @config_location = nil + @logger = logger || NullLogger.new + end + + def no_config_found? + config_location.nil? + end + + def config_location + @config_location ||= (explicit_config_file || locate_local_config) + end + + def chef_config_dir + if @chef_config_dir.nil? + @chef_config_dir = false + full_path = working_directory.split(File::SEPARATOR) + (full_path.length - 1).downto(0) do |i| + candidate_directory = File.join(full_path[0..i] + [".chef"]) + if File.exist?(candidate_directory) && File.directory?(candidate_directory) + @chef_config_dir = candidate_directory + break + end + end + end + @chef_config_dir + end + + def load + # Ignore it if there's no explicit_config_file and can't find one at a + # default path. + return false if config_location.nil? + + if explicit_config_file && !path_exists?(config_location) + raise ChefConfig::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_location + read_config(IO.read(config_location), config_location) + end + + # (Private API, public for test purposes) + def env + ENV + end + + # (Private API, public for test purposes) + def path_exists?(path) + Pathname.new(path).expand_path.exist? + end + + private + + def have_config?(path) + if path_exists?(path) + logger.info("Using config at #{path}") + true + else + logger.debug("Config not found at #{path}, trying next option") + false + end + end + + def locate_local_config + candidate_configs = [] + + # Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine) + if env['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.rb + if Dir.pwd + candidate_configs << File.join(Dir.pwd, 'config.rb') + candidate_configs << File.join(Dir.pwd, 'knife.rb') + end + # Look for $UPWARD/.chef/knife.rb + if chef_config_dir + candidate_configs << File.join(chef_config_dir, 'config.rb') + candidate_configs << File.join(chef_config_dir, 'knife.rb') + end + # Look for $HOME/.chef/knife.rb + PathHelper.home('.chef') do |dot_chef_dir| + candidate_configs << File.join(dot_chef_dir, 'config.rb') + candidate_configs << File.join(dot_chef_dir, 'knife.rb') + end + + candidate_configs.find do | candidate_config | + have_config?(candidate_config) + end + end + + def working_directory + a = if ChefConfig.windows? + env['CD'] + else + env['PWD'] + end || Dir.pwd + + a + end + + def read_config(config_content, config_file_path) + Config.from_string(config_content, config_file_path) + rescue SignalException + raise + rescue SyntaxError => e + message = "" + message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n" + message << "#{e.class.name}: #{e.message}\n" + if file_line = e.message[/#{Regexp.escape(config_file_path)}:[\d]+/] + line = file_line[/:([\d]+)$/, 1].to_i + message << highlight_config_error(config_file_path, line) + end + raise ChefConfig::ConfigurationError, message + rescue Exception => e + message = "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) + end + raise ChefConfig::ConfigurationError, message + end + + + def highlight_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}"} + if line == 1 + lines = config_file_lines[0..3] + else + lines = config_file_lines[Range.new(line - 2, line)] + end + "Relevant file content:\n" + lines.join("\n") + "\n" + end + + def logger + @logger + end + + end +end diff --git a/spec/unit/workstation_config_loader_spec.rb b/chef-config/spec/unit/workstation_config_loader_spec.rb index 72631f3dfa..f247d1cac2 100644 --- a/spec/unit/workstation_config_loader_spec.rb +++ b/chef-config/spec/unit/workstation_config_loader_spec.rb @@ -18,9 +18,12 @@ require 'spec_helper' require 'tempfile' -require 'chef/workstation_config_loader' -describe Chef::WorkstationConfigLoader do +require 'chef-config/exceptions' +require 'chef-config/windows' +require 'chef-config/workstation_config_loader' + +RSpec.describe ChefConfig::WorkstationConfigLoader do let(:explicit_config_location) { nil } @@ -65,7 +68,7 @@ describe Chef::WorkstationConfigLoader do let(:home) { "/Users/example.user" } before do - allow(Chef::Util::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef')) + allow(ChefConfig::PathHelper).to receive(:home).with('.chef').and_yield(File.join(home, '.chef')) allow(config_loader).to receive(:path_exists?).with("#{home}/.chef/knife.rb").and_return(true) end @@ -88,7 +91,7 @@ describe Chef::WorkstationConfigLoader do let(:env_pwd) { "/path/to/cwd" } before do - if Chef::Platform.windows? + if ChefConfig.windows? env["CD"] = env_pwd else env["PWD"] = env_pwd @@ -224,7 +227,7 @@ describe Chef::WorkstationConfigLoader do let(:explicit_config_location) { "/nope/nope/nope/frab/jab/nab" } it "raises a configuration error" do - expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError) + expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError) end end @@ -241,7 +244,7 @@ describe Chef::WorkstationConfigLoader do t.path end - after { File.unlink(explicit_config_location) if File.exists?(explicit_config_location) } + after { File.unlink(explicit_config_location) if File.exist?(explicit_config_location) } context "and is valid" do @@ -249,12 +252,12 @@ describe Chef::WorkstationConfigLoader do it "loads the config" do expect(config_loader.load).to be(true) - expect(Chef::Config.config_file_evaluated).to be(true) + expect(ChefConfig::Config.config_file_evaluated).to be(true) end - it "sets Chef::Config.config_file" do + it "sets ChefConfig::Config.config_file" do config_loader.load - expect(Chef::Config.config_file).to eq(explicit_config_location) + expect(ChefConfig::Config.config_file).to eq(explicit_config_location) end end @@ -263,7 +266,7 @@ describe Chef::WorkstationConfigLoader do let(:config_content) { "{{{{{:{{" } it "raises a ConfigurationError" do - expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError) + expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError) end end @@ -272,7 +275,7 @@ describe Chef::WorkstationConfigLoader do let(:config_content) { ":foo\n:bar\nraise 'oops'\n:baz\n" } it "raises a ConfigurationError" do - expect { config_loader.load }.to raise_error(Chef::Exceptions::ConfigurationError) + expect { config_loader.load }.to raise_error(ChefConfig::ConfigurationError) end end diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index dd0bac3cf9..9b5111c36d 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -17,12 +17,16 @@ # See the License for the specific language governing permissions and # limitations under the License. +require 'chef-config/exceptions' + class Chef # == Chef::Exceptions # Chef's custom exceptions are all contained within the Chef::Exceptions # namespace. class Exceptions + ConfigurationError = ChefConfig::ConfigurationError + # Backcompat with Chef::ShellOut code: require 'mixlib/shellout/exceptions' @@ -68,7 +72,6 @@ class Chef class DuplicateRole < RuntimeError; end class ValidationFailed < ArgumentError; end class InvalidPrivateKey < ArgumentError; end - class ConfigurationError < ArgumentError; end class MissingKeyAttribute < ArgumentError; end class KeyCommandInputError < ArgumentError; end class InvalidKeyArgument < ArgumentError; end diff --git a/lib/chef/workstation_config_loader.rb b/lib/chef/workstation_config_loader.rb index 2454c9cccf..8398c5d616 100644 --- a/lib/chef/workstation_config_loader.rb +++ b/lib/chef/workstation_config_loader.rb @@ -1,5 +1,5 @@ # -# Author:: Daniel DeLeo (<dan@getchef.com>) +# Author:: Claire McQuin (<claire@chef.io>) # Copyright:: Copyright (c) 2014 Chef Software, Inc. # License:: Apache License, Version 2.0 # @@ -16,163 +16,8 @@ # limitations under the License. # -require 'chef/config_fetcher' -require 'chef/config' -require 'chef/null_logger' -require 'chef/util/path_helper' +require 'chef-config/workstation_config_loader' class Chef - - class WorkstationConfigLoader - - # Path to a config file requested by user, (e.g., via command line option). Can be nil - attr_accessor :explicit_config_file - - # TODO: initialize this with a logger for Chef and Knife - def initialize(explicit_config_file, logger=nil) - @explicit_config_file = explicit_config_file - @config_location = nil - @logger = logger || NullLogger.new - end - - def no_config_found? - config_location.nil? - end - - def config_location - @config_location ||= (explicit_config_file || locate_local_config) - end - - def chef_config_dir - if @chef_config_dir.nil? - @chef_config_dir = false - full_path = working_directory.split(File::SEPARATOR) - (full_path.length - 1).downto(0) do |i| - candidate_directory = File.join(full_path[0..i] + [".chef" ]) - if File.exist?(candidate_directory) && File.directory?(candidate_directory) - @chef_config_dir = candidate_directory - break - end - end - end - @chef_config_dir - end - - def load - # Ignore it if there's no explicit_config_file and can't find one at a - # default path. - return false if config_location.nil? - - if explicit_config_file && !path_exists?(config_location) - raise Exceptions::ConfigurationError, "Specified config file #{config_location} does not exist" - end - - # Have to set Chef::Config.config_file b/c other config is derived from it. - Chef::Config.config_file = config_location - read_config(IO.read(config_location), config_location) - end - - # (Private API, public for test purposes) - def env - ENV - end - - # (Private API, public for test purposes) - def path_exists?(path) - Pathname.new(path).expand_path.exist? - end - - private - - def have_config?(path) - if path_exists?(path) - logger.info("Using config at #{path}") - true - else - logger.debug("Config not found at #{path}, trying next option") - false - end - end - - def locate_local_config - candidate_configs = [] - - # Look for $KNIFE_HOME/knife.rb (allow multiple knives config on same machine) - if env['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.rb - if Dir.pwd - candidate_configs << File.join(Dir.pwd, 'config.rb') - candidate_configs << File.join(Dir.pwd, 'knife.rb') - end - # Look for $UPWARD/.chef/knife.rb - if chef_config_dir - candidate_configs << File.join(chef_config_dir, 'config.rb') - candidate_configs << File.join(chef_config_dir, 'knife.rb') - end - # Look for $HOME/.chef/knife.rb - Chef::Util::PathHelper.home('.chef') do |dot_chef_dir| - candidate_configs << File.join(dot_chef_dir, 'config.rb') - candidate_configs << File.join(dot_chef_dir, 'knife.rb') - end - - candidate_configs.find do | candidate_config | - have_config?(candidate_config) - end - end - - def working_directory - a = if Chef::Platform.windows? - env['CD'] - else - env['PWD'] - end || Dir.pwd - - a - end - - def read_config(config_content, config_file_path) - Chef::Config.from_string(config_content, config_file_path) - rescue SignalException - raise - rescue SyntaxError => e - message = "" - message << "You have invalid ruby syntax in your config file #{config_file_path}\n\n" - message << "#{e.class.name}: #{e.message}\n" - if file_line = e.message[/#{Regexp.escape(config_file_path)}:[\d]+/] - line = file_line[/:([\d]+)$/, 1].to_i - message << highlight_config_error(config_file_path, line) - end - raise Exceptions::ConfigurationError, message - rescue Exception => e - message = "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) - end - raise Exceptions::ConfigurationError, message - end - - - def highlight_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}"} - if line == 1 - lines = config_file_lines[0..3] - else - lines = config_file_lines[Range.new(line - 2, line)] - end - "Relevant file content:\n" + lines.join("\n") + "\n" - end - - def logger - @logger - end - - end + WorkstationConfigLoader = ChefConfig::WorkstationConfigLoader end |