diff options
author | Thom May <thom@may.lt> | 2017-12-18 18:31:58 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-18 18:31:58 +0000 |
commit | 925051d67b36a2b6c6a13c99cccf4109e99987f5 (patch) | |
tree | 087260689310c208d67eed36cab4e7cd62e9f5fd | |
parent | eccc48d63e3bc99d161ad6ca48f7770fcd2b9b59 (diff) | |
parent | 92671398a99bb4393650f4343c3b2a20814eb3eb (diff) | |
download | chef-925051d67b36a2b6c6a13c99cccf4109e99987f5.tar.gz |
Merge pull request #6660 from chef/tm/credentials
implement credential management
-rw-r--r-- | Gemfile.lock | 1 | ||||
-rw-r--r-- | chef-config/chef-config.gemspec | 1 | ||||
-rw-r--r-- | chef-config/lib/chef-config/config.rb | 10 | ||||
-rw-r--r-- | chef-config/lib/chef-config/mixin/credentials.rb | 57 | ||||
-rw-r--r-- | chef-config/lib/chef-config/workstation_config_loader.rb | 44 | ||||
-rw-r--r-- | chef-config/spec/unit/workstation_config_loader_spec.rb | 144 | ||||
-rw-r--r-- | lib/chef/application/knife.rb | 4 | ||||
-rw-r--r-- | lib/chef/knife.rb | 5 | ||||
-rw-r--r-- | lib/chef/knife/configure.rb | 48 | ||||
-rw-r--r-- | lib/chef/server_api.rb | 1 | ||||
-rw-r--r-- | spec/unit/knife/configure_spec.rb | 70 | ||||
-rw-r--r-- | spec/unit/knife_spec.rb | 2 |
12 files changed, 287 insertions, 100 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index 401a1f95f0..e9881a050f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,6 +97,7 @@ PATH fuzzyurl mixlib-config (~> 2.0) mixlib-shellout (~> 2.0) + tomlrb (~> 1.2) GEM remote: https://rubygems.org/ diff --git a/chef-config/chef-config.gemspec b/chef-config/chef-config.gemspec index 9e40528fba..1dc1a118ff 100644 --- a/chef-config/chef-config.gemspec +++ b/chef-config/chef-config.gemspec @@ -19,6 +19,7 @@ Gem::Specification.new do |spec| spec.add_dependency "mixlib-config", "~> 2.0" spec.add_dependency "fuzzyurl" spec.add_dependency "addressable" + spec.add_dependency "tomlrb", "~> 1.2" spec.add_development_dependency "rake", "~> 10.0" diff --git a/chef-config/lib/chef-config/config.rb b/chef-config/lib/chef-config/config.rb index beb78f25d0..4855533266 100644 --- a/chef-config/lib/chef-config/config.rb +++ b/chef-config/lib/chef-config/config.rb @@ -592,6 +592,12 @@ module ChefConfig # If chef-zero is enabled, this defaults to nil (no authentication). default(:client_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/client.pem") } + # A credentials file may contain a complete client key, rather than the path + # to one. + # + # We'll use this preferentially. + default :client_key_contents, nil + # When registering the client, should we allow the client key location to # be a symlink? eg: /etc/chef/client.pem -> /etc/chef/prod-client.pem # If the path of the key goes through a directory like /tmp this should @@ -631,6 +637,7 @@ module ChefConfig default(:validation_key) { chef_zero.enabled ? nil : platform_specific_path("/etc/chef/validation.pem") } default :validation_client_name, "chef-validator" + default :validation_key_contents, nil # When creating a new client via the validation_client account, Chef 11 # servers allow the client to generate a key pair locally and send the # public key to the server. This is more secure and helps offload work from @@ -691,6 +698,9 @@ module ChefConfig # on running chef-client default :count_log_resource_updates, true + # The selected profile when using credentials. + default :profile, nil + # knife configuration data config_context :knife do # XXX: none of these default values are applied to knife (and would create a backcompat diff --git a/chef-config/lib/chef-config/mixin/credentials.rb b/chef-config/lib/chef-config/mixin/credentials.rb new file mode 100644 index 0000000000..4c0192fff8 --- /dev/null +++ b/chef-config/lib/chef-config/mixin/credentials.rb @@ -0,0 +1,57 @@ +# +# Copyright:: Copyright 2017, 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 "tomlrb" +require "chef-config/path_helper" + +module ChefConfig + module Mixin + module Credentials + + def load_credentials(profile = nil) + credentials_file = PathHelper.home(".chef", "credentials").freeze + context_file = PathHelper.home(".chef", "context").freeze + + return unless File.file?(credentials_file) + + context = File.read(context_file) if File.file?(context_file) + + environment = ENV.fetch("CHEF_PROFILE", nil) + + profile = if !profile.nil? + profile + elsif !environment.nil? + environment + elsif !context.nil? + context + else + "default" + end + + config = Tomlrb.load_file(credentials_file) + apply_credentials(config[profile], profile) + rescue ChefConfig::ConfigurationError + raise + rescue => e + # TOML's error messages are mostly rubbish, so we'll just give a generic one + message = "Unable to parse Credentials file: #{credentials_file}\n" + message << e.message + raise ChefConfig::ConfigurationError, message + end + end + end +end diff --git a/chef-config/lib/chef-config/workstation_config_loader.rb b/chef-config/lib/chef-config/workstation_config_loader.rb index babb78aeb8..2738d6a1a2 100644 --- a/chef-config/lib/chef-config/workstation_config_loader.rb +++ b/chef-config/lib/chef-config/workstation_config_loader.rb @@ -22,24 +22,31 @@ require "chef-config/logger" require "chef-config/path_helper" require "chef-config/windows" require "chef-config/mixin/dot_d" +require "chef-config/mixin/credentials" module ChefConfig class WorkstationConfigLoader include ChefConfig::Mixin::DotD + include ChefConfig::Mixin::Credentials # Path to a config file requested by user, (e.g., via command line option). Can be nil attr_accessor :explicit_config_file + # The name of a credentials profile. Can be nil + attr_accessor :profile + attr_reader :credentials_found # TODO: initialize this with a logger for Chef and Knife - def initialize(explicit_config_file, logger = nil) + def initialize(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 = false end def no_config_found? - config_location.nil? + config_location.nil? && !credentials_found end def config_location @@ -62,6 +69,7 @@ module ChefConfig end def load + load_credentials(profile) # Ignore it if there's no explicit_config_file and can't find one at a # default path. if !config_location.nil? @@ -138,6 +146,38 @@ module ChefConfig a end + def apply_credentials(creds, profile) + Config.profile ||= profile + if creds.key?("node_name") && creds.key?("client_name") + raise ChefConfig::ConfigurationError, "Do not specify both node_name and client_name. You should prefer client_name." + end + Config.node_name = creds.fetch("node_name") if creds.key?("node_name") + Config.node_name = creds.fetch("client_name") if creds.key?("client_name") + Config.chef_server_url = creds.fetch("chef_server_url") if creds.key?("chef_server_url") + Config.validation_client_name = creds.fetch("validation_client_name") if creds.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 = true + end + + def extract_key(creds, name, config_path, config_contents) + return unless creds.has_key?(name) + + val = creds.fetch(name) + if val.start_with?("-----BEGIN RSA PRIVATE KEY-----") + Config.send(config_contents, val) + else + abs_path = Pathname.new(val).expand_path(home_chef_dir) + Config.send(config_path, abs_path) + end + end + + def home_chef_dir + @home_chef_dir ||= PathHelper.home(".chef") + end + def apply_config(config_content, config_file_path) Config.from_string(config_content, config_file_path) rescue SignalException diff --git a/chef-config/spec/unit/workstation_config_loader_spec.rb b/chef-config/spec/unit/workstation_config_loader_spec.rb index 087f249724..509d95fe36 100644 --- a/chef-config/spec/unit/workstation_config_loader_spec.rb +++ b/chef-config/spec/unit/workstation_config_loader_spec.rb @@ -38,6 +38,7 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do before do # We set this to nil so that a dev workstation will # not interfere with the tests. + ChefConfig::Config.reset ChefConfig::Config[:config_d_dir] = nil end @@ -363,4 +364,147 @@ RSpec.describe ChefConfig::WorkstationConfigLoader do end end end + + describe "when loading a credentials file" do + if ChefConfig.windows? + let(:home) { "C:/Users/example.user" } + else + let(:home) { "/Users/example.user" } + end + let(:credentials_file) { "#{home}/.chef/credentials" } + let(:context_file) { "#{home}/.chef/context" } + + before do + allow(ChefConfig::PathHelper).to receive(:home).with(".chef").and_return(File.join(home, ".chef")) + allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "credentials").and_return(credentials_file) + allow(ChefConfig::PathHelper).to receive(:home).with(".chef", "context").and_return(context_file) + allow(File).to receive(:file?).with(context_file).and_return false + end + + context "when the file exists" do + before do + expect(File).to receive(:read).with(credentials_file, { encoding: "utf-8" }).and_return(content) + allow(File).to receive(:file?).with(credentials_file).and_return true + end + + context "and has a default profile" do + let(:content) do + content = <<EOH +[default] +node_name = 'barney' +client_key = "barney_rubble.pem" +chef_server_url = "https://api.chef.io/organizations/bedrock" +EOH + content + end + + it "applies the expected config" do + expect { config_loader.load_credentials }.not_to raise_error + expect(ChefConfig::Config.chef_server_url).to eq("https://api.chef.io/organizations/bedrock") + expect(ChefConfig::Config.client_key.to_s).to eq("#{home}/.chef/barney_rubble.pem") + expect(ChefConfig::Config.profile.to_s).to eq("default") + end + end + + context "and has a profile containing a full key" do + let(:content) do + content = <<EOH +[default] +client_key = """ +-----BEGIN RSA PRIVATE KEY----- +foo +""" +EOH + content + end + + it "applies the expected config" do + expect { config_loader.load_credentials }.not_to raise_error + expect(ChefConfig::Config.client_key_contents).to eq(<<EOH +-----BEGIN RSA PRIVATE KEY----- +foo +EOH +) + end + end + + context "and has several profiles" do + let(:content) do + content = <<EOH +[default] +client_name = "default" +[environment] +client_name = "environment" +[explicit] +client_name = "explicit" +[context] +client_name = "context" +EOH + content + end + + let(:env) { {} } + before do + stub_const("ENV", env) + end + + it "selects the correct profile explicitly" do + expect { config_loader.load_credentials("explicit") }.not_to raise_error + expect(ChefConfig::Config.node_name).to eq("explicit") + end + + context "with an environment variable" do + let(:env) { { "CHEF_PROFILE" => "environment" } } + + it "selects the correct profile" do + expect { config_loader.load_credentials }.not_to raise_error + expect(ChefConfig::Config.node_name).to eq("environment") + end + end + + it "selects the correct profile with a context file" do + allow(File).to receive(:file?).with(context_file).and_return true + expect(File).to receive(:read).with(context_file).and_return "context" + expect { config_loader.load_credentials }.not_to raise_error + expect(ChefConfig::Config.node_name).to eq("context") + end + + it "falls back to the default" do + expect { config_loader.load_credentials }.not_to raise_error + expect(ChefConfig::Config.node_name).to eq("default") + end + end + + context "and contains both node_name and client_name" do + let(:content) do + content = <<EOH +[default] +node_name = 'barney' +client_name = 'barney' +EOH + content + end + + it "raises a ConfigurationError" do + expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError) + end + end + + context "and has a syntax error" do + let(:content) { "<<<<<" } + + it "raises a ConfigurationError" do + expect { config_loader.load_credentials }.to raise_error(ChefConfig::ConfigurationError) + end + end + end + + context "when the file does not exist" do + it "does not load anything" do + allow(File).to receive(:file?).with(credentials_file).and_return false + expect(Tomlrb).not_to receive(:load_file) + config_loader.load_credentials + end + end + end end diff --git a/lib/chef/application/knife.rb b/lib/chef/application/knife.rb index 6a09427ccd..c972e9313e 100644 --- a/lib/chef/application/knife.rb +++ b/lib/chef/application/knife.rb @@ -148,6 +148,10 @@ class Chef::Application::Knife < Chef::Application :boolean => true, :default => nil + option :profile, + :long => "--profile PROFILE", + :description => "The credentials profile to select" + # Run knife def run Mixlib::Log::Formatter.show_time = false diff --git a/lib/chef/knife.rb b/lib/chef/knife.rb index 65d687af70..663649f32f 100644 --- a/lib/chef/knife.rb +++ b/lib/chef/knife.rb @@ -177,8 +177,9 @@ class Chef @config_loader ||= WorkstationConfigLoader.new(nil, Chef::Log) end - def self.load_config(explicit_config_file) + def self.load_config(explicit_config_file, profile) config_loader.explicit_config_file = explicit_config_file + config_loader.profile = profile config_loader.load ui.warn("No knife configuration file found") if config_loader.no_config_found? @@ -404,7 +405,7 @@ class Chef def configure_chef # knife needs to send logger output to STDERR by default Chef::Config[:log_location] = STDERR - config_loader = self.class.load_config(config[:config_file]) + config_loader = self.class.load_config(config[:config_file], config[:profile]) config[:config_file] = config_loader.config_location # For CLI options like `--config-option key=value`. These have to get diff --git a/lib/chef/knife/configure.rb b/lib/chef/knife/configure.rb index 967a18de87..10ae62b6c9 100644 --- a/lib/chef/knife/configure.rb +++ b/lib/chef/knife/configure.rb @@ -17,6 +17,7 @@ # require "chef/knife" +require "chef/util/path_helper" class Chef class Knife @@ -67,24 +68,22 @@ class Chef end def run - ask_user_for_config_path - FileUtils.mkdir_p(chef_config_path) + config_file = File.join(chef_config_path, "credentials") ask_user_for_config - ::File.open(config[:config_file], "w") do |f| + config_file = File.expand_path(config_file) + if File.exist?(config_file) + confirm("Overwrite #{config_file}?") + end + ::File.open(config_file, "w") do |f| f.puts <<-EOH -node_name '#{new_client_name}' -client_key '#{new_client_key}' -validation_client_name '#{validation_client_name}' -validation_key '#{validation_key}' -chef_server_url '#{chef_server}' -syntax_check_cache_path '#{File.join(chef_config_path, "syntax_check_cache")}' +[default] +client_name = '#{new_client_name}' +client_key = '#{new_client_key}' +chef_server_url = '#{chef_server}' EOH - unless chef_repo.empty? - f.puts "cookbook_path [ '#{chef_repo}/cookbooks' ]" - end end if config[:initial] @@ -109,26 +108,11 @@ EOH ui.msg("Before running commands with Knife") ui.msg("") ui.msg("*****") - ui.msg("") - ui.msg("You must place your validation key in:") - ui.msg(" #{validation_key}") - ui.msg("Before generating instance data with Knife") - ui.msg("") - ui.msg("*****") end ui.msg("Configuration file written to #{config[:config_file]}") end - def ask_user_for_config_path - config[:config_file] ||= ask_question("Where should I put the config file? ", :default => "#{Chef::Config[:user_home]}/.chef/knife.rb") - # have to use expand path to expand the tilde character to the user's home - config[:config_file] = File.expand_path(config[:config_file]) - if File.exists?(config[:config_file]) - confirm("Overwrite #{config[:config_file]}") - end - end - def ask_user_for_config server_name = guess_servername @chef_server = config[:chef_server_url] || ask_question("Please enter the chef server URL: ", :default => "https://#{server_name}/organizations/myorg") @@ -140,10 +124,6 @@ EOH else @new_client_name = config[:node_name] || ask_question("Please enter an existing username or clientname for the API: ", :default => Etc.getlogin) end - @validation_client_name = config[:validation_client_name] || ask_question("Please enter the validation clientname: ", :default => "chef-validator") - @validation_key = config[:validation_key] || ask_question("Please enter the location of the validation key: ", :default => "/etc/chef-server/chef-validator.pem") - @validation_key = File.expand_path(@validation_key) - @chef_repo = config[:repository] || ask_question("Please enter the path to a chef repository (or leave blank): ") @new_client_key = config[:client_key] || File.join(chef_config_path, "#{@new_client_name}.pem") @new_client_key = File.expand_path(@new_client_key) @@ -157,12 +137,8 @@ EOH o[:fqdn] || o[:machinename] || o[:hostname] || "localhost" end - def config_file - config[:config_file] - end - def chef_config_path - File.dirname(config_file) + Chef::Util::PathHelper.home(".chef") end end end diff --git a/lib/chef/server_api.rb b/lib/chef/server_api.rb index 2bdc5d9fe8..c501544954 100644 --- a/lib/chef/server_api.rb +++ b/lib/chef/server_api.rb @@ -31,6 +31,7 @@ class Chef def initialize(url = Chef::Config[:chef_server_url], options = {}) options[:client_name] ||= Chef::Config[:node_name] + options[:raw_key] ||= Chef::Config[:client_key_contents] options[:signing_key_filename] ||= Chef::Config[:client_key] unless options[:raw_key] options[:signing_key_filename] = nil if chef_zero_uri?(url) options[:inflate_json_class] = false diff --git a/spec/unit/knife/configure_spec.rb b/spec/unit/knife/configure_spec.rb index b7802d3890..f1d3bd0745 100644 --- a/spec/unit/knife/configure_spec.rb +++ b/spec/unit/knife/configure_spec.rb @@ -124,55 +124,12 @@ describe Chef::Knife::Configure do end end - it "asks the user for the location of a chef repo" do - @knife.ask_user_for_config - expect(@out.string).to match(Regexp.escape("Please enter the path to a chef repository (or leave blank):")) - expect(@knife.chef_repo).to eq("") - end - - it "asks the users for the name of the validation client" do - @knife.ask_user_for_config - expect(@out.string).to match(Regexp.escape("Please enter the validation clientname: [chef-validator]")) - expect(@knife.validation_client_name).to eq("chef-validator") - end - - it "should not ask the users for the name of the validation client if --validation_client_name is specified" do - @knife.config[:validation_client_name] = "my-validator" - @knife.ask_user_for_config - expect(@out.string).not_to match(Regexp.escape("Please enter the validation clientname:")) - expect(@knife.validation_client_name).to eq("my-validator") - end - - it "asks the users for the location of the validation key" do - @knife.ask_user_for_config - expect(@out.string).to match(Regexp.escape("Please enter the location of the validation key: [#{default_validator_key}]")) - if windows? - expect(@knife.validation_key.capitalize).to eq(default_validator_key_win32.capitalize) - else - expect(@knife.validation_key).to eq(default_validator_key) - end - end - - it "should not ask the users for the location of the validation key if --validation_key is specified" do - @knife.config[:validation_key] = "/home/you/.chef/my-validation.pem" - @knife.ask_user_for_config - expect(@out.string).not_to match(Regexp.escape("Please enter the location of the validation key:")) - if windows? - expect(@knife.validation_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-validation\.pem$} - else - expect(@knife.validation_key).to eq("/home/you/.chef/my-validation.pem") - end - end - it "should not ask the user for anything if -i and all other properties are specified" do @knife.config[:initial] = true @knife.config[:chef_server_url] = "http://localhost:5000" @knife.config[:node_name] = "testnode" @knife.config[:admin_client_name] = "my-webui" @knife.config[:admin_client_key] = "/home/you/.chef/my-webui.pem" - @knife.config[:validation_client_name] = "my-validator" - @knife.config[:validation_key] = "/home/you/.chef/my-validation.pem" - @knife.config[:repository] = "" @knife.config[:client_key] = "/home/you/a-new-user.pem" allow(Etc).to receive(:getlogin).and_return("a-new-user") @@ -184,40 +141,33 @@ describe Chef::Knife::Configure do expect(@knife.admin_client_name).to eq("my-webui") if windows? expect(@knife.admin_client_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-webui\.pem$} - expect(@knife.validation_key).to match %r{^[A-Za-z]:/home/you/\.chef/my-validation\.pem$} expect(@knife.new_client_key).to match %r{^[A-Za-z]:/home/you/a-new-user\.pem$} else expect(@knife.admin_client_key).to eq("/home/you/.chef/my-webui.pem") - expect(@knife.validation_key).to eq("/home/you/.chef/my-validation.pem") expect(@knife.new_client_key).to eq("/home/you/a-new-user.pem") end - expect(@knife.validation_client_name).to eq("my-validator") - expect(@knife.chef_repo).to eq("") end it "writes the new data to a config file" do - allow(File).to receive(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb") + allow(Chef::Util::PathHelper).to receive(:home).with(".chef").and_return("/home/you/.chef") + allow(File).to receive(:expand_path).with("/home/you/.chef/credentials").and_return("/home/you/.chef/credentials") allow(File).to receive(:expand_path).with("/home/you/.chef/#{Etc.getlogin}.pem").and_return("/home/you/.chef/#{Etc.getlogin}.pem") - allow(File).to receive(:expand_path).with(default_validator_key).and_return(default_validator_key) allow(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key) expect(FileUtils).to receive(:mkdir_p).with("/home/you/.chef") config_file = StringIO.new - expect(::File).to receive(:open).with("/home/you/.chef/knife.rb", "w").and_yield config_file + expect(::File).to receive(:open).with("/home/you/.chef/credentials", "w").and_yield config_file @knife.config[:repository] = "/home/you/chef-repo" @knife.run - expect(config_file.string).to match(/^node_name[\s]+'#{Etc.getlogin}'$/) - expect(config_file.string).to match(%r{^client_key[\s]+'/home/you/.chef/#{Etc.getlogin}.pem'$}) - expect(config_file.string).to match(/^validation_client_name\s+'chef-validator'$/) - expect(config_file.string).to match(%r{^validation_key\s+'#{default_validator_key}'$}) - expect(config_file.string).to match(%r{^chef_server_url\s+'#{default_server_url}'$}) - expect(config_file.string).to match(%r{cookbook_path\s+\[ '/home/you/chef-repo/cookbooks' \]}) + expect(config_file.string).to match(/^client_name[\s]+=[\s]+'#{Etc.getlogin}'$/) + expect(config_file.string).to match(%r{^client_key[\s]+=[\s]+'/home/you/.chef/#{Etc.getlogin}.pem'$}) + expect(config_file.string).to match(%r{^chef_server_url\s+=[\s]+'#{default_server_url}'$}) end it "creates a new client when given the --initial option" do - expect(File).to receive(:expand_path).with("/home/you/.chef/knife.rb").and_return("/home/you/.chef/knife.rb") + allow(Chef::Util::PathHelper).to receive(:home).with(".chef").and_return("/home/you/.chef") + expect(File).to receive(:expand_path).with("/home/you/.chef/credentials").and_return("/home/you/.chef/credentials") expect(File).to receive(:expand_path).with("/home/you/.chef/a-new-user.pem").and_return("/home/you/.chef/a-new-user.pem") - expect(File).to receive(:expand_path).with(default_validator_key).and_return(default_validator_key) - expect(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key) + allow(File).to receive(:expand_path).with(default_admin_key).and_return(default_admin_key) Chef::Config[:node_name] = "webmonkey.example.com" user_command = Chef::Knife::UserCreate.new @@ -227,7 +177,7 @@ describe Chef::Knife::Configure do allow(Chef::Knife::UserCreate).to receive(:new).and_return(user_command) expect(FileUtils).to receive(:mkdir_p).with("/home/you/.chef") - expect(::File).to receive(:open).with("/home/you/.chef/knife.rb", "w") + expect(::File).to receive(:open).with("/home/you/.chef/credentials", "w") @knife.config[:initial] = true @knife.config[:user_password] = "blah" @knife.run diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb index 2b22dbc4f7..adaab11d55 100644 --- a/spec/unit/knife_spec.rb +++ b/spec/unit/knife_spec.rb @@ -47,6 +47,7 @@ describe Chef::Knife do allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader) allow(config_loader).to receive(:explicit_config_file=) + allow(config_loader).to receive(:profile=) # Prevent gratuitous code reloading: allow(Chef::Knife).to receive(:load_commands) @@ -331,6 +332,7 @@ describe Chef::Knife do knife.config[:config_file] = fake_config config_loader = double("Chef::WorkstationConfigLoader", :load => true, :no_config_found? => false, :chef_config_dir => "/etc/chef", :config_location => fake_config) allow(config_loader).to receive(:explicit_config_file=).with(fake_config).and_return(fake_config) + allow(config_loader).to receive(:profile=) allow(Chef::WorkstationConfigLoader).to receive(:new).and_return(config_loader) end |