File Manager
Viewing File: application_spec.rb
#
# Author:: AJ Christensen (<aj@junglist.gen.nz>)
# Author:: Mark Mzyk (mmzyk@chef.io)
# Copyright:: Copyright 2008-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 "spec_helper"
describe Chef::Application do
before do
@original_argv = ARGV.dup
ARGV.clear
Chef::Log.logger = Logger.new(StringIO.new)
@app = Chef::Application.new
allow(@app).to receive(:trap)
allow(Dir).to receive(:chdir).and_return(0)
allow(@app).to receive(:reconfigure)
Chef::Log.init(STDERR)
end
after do
ARGV.replace(@original_argv)
end
context "when there are no configuration errors" do
before do
expect(Chef::Log).to_not receive(:fatal)
expect(Chef::Application).to_not receive(:fatal!)
end
describe "reconfigure" do
before do
@app = Chef::Application.new
allow(@app).to receive(:configure_chef).and_return(true)
allow(@app).to receive(:configure_logging).and_return(true)
end
it "should configure chef" do
expect(@app).to receive(:configure_chef).and_return(true)
@app.reconfigure
end
it "should configure logging" do
expect(@app).to receive(:configure_logging).and_return(true)
@app.reconfigure
end
it "should not receive set_specific_recipes" do
expect(@app).to_not receive(:set_specific_recipes)
@app.reconfigure
end
end
describe Chef::Application do
before do
@app = Chef::Application.new
end
describe "run" do
before do
allow(@app).to receive(:setup_application).and_return(true)
allow(@app).to receive(:run_application).and_return(true)
allow(@app).to receive(:configure_chef).and_return(true)
allow(@app).to receive(:configure_logging).and_return(true)
end
it "should reconfigure the application before running" do
expect(@app).to receive(:reconfigure).and_return(true)
@app.run
end
it "should setup the application before running it" do
expect(@app).to receive(:setup_application).and_return(true)
@app.run
end
it "should run the actual application" do
expect(@app).to receive(:run_application).and_return(true)
@app.run
end
end
end
describe "configure_chef" do
before do
# Silence warnings when no config file exists
allow(Chef::Log).to receive(:warn)
@app = Chef::Application.new
allow(@app).to receive(:parse_options).and_return(true)
expect(Chef::Config).to receive(:export_proxies).and_return(true)
end
it "should parse the commandline options" do
expect(@app).to receive(:parse_options).and_return(true)
@app.config[:config_file] = "/etc/chef/default.rb" #have a config file set, to prevent triggering error block
@app.configure_chef
end
describe "when a config_file is present" do
let(:config_content) { "rspec_ran('true')" }
let(:config_location) { "/etc/chef/default.rb" }
let(:config_location_pathname) do
p = Pathname.new(config_location)
allow(p).to receive(:realpath).and_return(config_location)
p
end
before do
@app.config[:config_file] = config_location
# force let binding to get evaluated or else we stub Pathname.new before we try to use it.
config_location_pathname
allow(Pathname).to receive(:new).with(config_location).and_return(config_location_pathname)
expect(File).to receive(:read).
with(config_location).
and_return(config_content)
end
it "should configure chef::config from a file" do
expect(Chef::Config).to receive(:from_string).with(config_content, File.expand_path(config_location))
@app.configure_chef
end
it "should merge the local config hash into chef::config" do
#File.should_receive(:open).with("/etc/chef/default.rb").and_yield(@config_file)
@app.configure_chef
expect(Chef::Config.rspec_ran).to eq("true")
end
context "when openssl fips" do
before do
allow(Chef::Config).to receive(:fips).and_return(true)
end
it "sets openssl in fips mode" do
expect(Chef::Config).to receive(:enable_fips_mode)
@app.configure_chef
end
end
end
describe "when there is no config_file defined" do
before do
@app.config[:config_file] = nil
end
it "should emit a warning" do
expect(Chef::Config).not_to receive(:from_file).with("/etc/chef/default.rb")
expect(Chef::Log).to receive(:warn).with("No config file found or specified on command line, using command line options.")
@app.configure_chef
end
end
describe "when the config file is set and not found" do
before do
@app.config[:config_file] = "/etc/chef/notfound"
end
it "should use the passed in command line options and defaults" do
expect(Chef::Config).to receive(:merge!)
@app.configure_chef
end
end
end
describe "when configuring the logger" do
before do
@app = Chef::Application.new
allow(Chef::Log).to receive(:init)
end
it "should initialise the chef logger" do
allow(Chef::Log).to receive(:level=)
@monologger = double("Monologger")
expect(MonoLogger).to receive(:new).with(Chef::Config[:log_location]).and_return(@monologger)
expect(Chef::Log).to receive(:init).with(@monologger)
@app.configure_logging
end
shared_examples_for "log_level_is_auto" do
context "when STDOUT is to a tty" do
before do
allow(STDOUT).to receive(:tty?).and_return(true)
end
it "configures the log level to :warn" do
@app.configure_logging
expect(Chef::Log.level).to eq(:warn)
end
context "when force_logger is configured" do
before do
Chef::Config[:force_logger] = true
end
it "configures the log level to info" do
@app.configure_logging
expect(Chef::Log.level).to eq(:info)
end
end
end
context "when STDOUT is not to a tty" do
before do
allow(STDOUT).to receive(:tty?).and_return(false)
end
it "configures the log level to :info" do
@app.configure_logging
expect(Chef::Log.level).to eq(:info)
end
context "when force_formatter is configured" do
before do
Chef::Config[:force_formatter] = true
end
it "sets the log level to :warn" do
@app.configure_logging
expect(Chef::Log.level).to eq(:warn)
end
end
end
end
context "when log_level is not set" do
it_behaves_like "log_level_is_auto"
end
context "when log_level is :auto" do
before do
Chef::Config[:log_level] = :auto
end
it_behaves_like "log_level_is_auto"
end
describe "log_location" do
shared_examples("sets log_location") do |config_value, expected_class|
context "when the configured value is #{config_value.inspect}" do
let(:logger_instance) { instance_double(expected_class).as_null_object }
before do
allow(expected_class).to receive(:new).and_return(logger_instance)
Chef::Config[:log_location] = config_value
end
it "it sets log_location to an instance of #{expected_class}" do
expect(expected_class).to receive(:new).with no_args
@app.configure_logging
expect(Chef::Config[:log_location]).to be logger_instance
end
end
end
if Chef::Platform.windows?
it_behaves_like "sets log_location", :win_evt, Chef::Log::WinEvt
it_behaves_like "sets log_location", "win_evt", Chef::Log::WinEvt
else
it_behaves_like "sets log_location", :syslog, Chef::Log::Syslog
it_behaves_like "sets log_location", "syslog", Chef::Log::Syslog
end
end
end
end
context "with an invalid log location" do
it "logs a fatal error and exits" do
Chef::Config[:log_location] = "/tmp/non-existing-dir/logfile"
expect(Chef::Log).to receive(:fatal).at_least(:once)
expect(Process).to receive(:exit)
@app.configure_logging
end
end
describe "class method: fatal!" do
before do
allow(STDERR).to receive(:puts).with("FATAL: blah").and_return(true)
allow(Chef::Log).to receive(:fatal).and_return(true)
allow(Process).to receive(:exit).and_return(true)
end
it "should log an error message to the logger" do
expect(Chef::Log).to receive(:fatal).with("blah").and_return(true)
Chef::Application.fatal! "blah"
end
describe "when an exit code is supplied" do
it "should exit with the given exit code" do
expect(Process).to receive(:exit).with(-100).and_return(true)
Chef::Application.fatal! "blah", -100
end
end
describe "when an exit code is not supplied" do
it "should exit with the default exit code" do
expect(Process).to receive(:exit).with(-1).and_return(true)
Chef::Application.fatal! "blah"
end
end
end
describe "setup_application" do
before do
@app = Chef::Application.new
end
it "should raise an error" do
expect { @app.setup_application }.to raise_error(Chef::Exceptions::Application)
end
end
describe "run_application" do
before do
@app = Chef::Application.new
end
it "should raise an error" do
expect { @app.run_application }.to raise_error(Chef::Exceptions::Application)
end
end
context "when the config file is not available" do
it "should warn for bad config file path" do
@app.config[:config_file] = "/tmp/non-existing-dir/file"
config_file_regexp = Regexp.new @app.config[:config_file]
expect(Chef::Log).to receive(:warn).at_least(:once).with(config_file_regexp).and_return(true)
allow(Chef::Log).to receive(:warn).and_return(true)
@app.configure_chef
end
end
describe "run_chef_client" do
context "with an application" do
let(:app) { Chef::Application.new }
context "when called with an invalid argument" do
before do
allow(app).to receive(:fork_chef_client).and_return(true)
allow(app).to receive(:run_with_graceful_exit_option).and_return(true)
end
it "should raise an argument error detailing the problem" do
specific_recipes_regexp = Regexp.new "received non-Array like specific_recipes argument"
expect { app.run_chef_client(nil) }.to raise_error(ArgumentError, specific_recipes_regexp)
end
end
context "when called with an Array-like argument (#size)" do
before do
allow(app).to receive(:fork_chef_client).and_return(true)
allow(app).to receive(:run_with_graceful_exit_option).and_return(true)
end
it "should be cool" do
expect { app.run_chef_client([]) }.not_to raise_error
end
end
end
end
describe "configuration errors" do
before do
expect(Process).to receive(:exit)
end
def raises_informative_fatals_on_configure_chef
config_file_regexp = Regexp.new @app.config[:config_file]
expect(Chef::Log).to receive(:fatal).
with(/Configuration error/)
expect(Chef::Log).to receive(:fatal).
with(config_file_regexp).
at_least(1).times
@app.configure_chef
end
describe "when config file exists but contains errors" do
def create_config_file(text)
@config_file = Tempfile.new("rspec-chef-config")
@config_file.write(text)
@config_file.close
@app.config[:config_file] = @config_file.path
end
after(:each) do
@config_file.unlink
end
it "should raise informative fatals for badly written config" do
create_config_file("text that should break the config parsing")
raises_informative_fatals_on_configure_chef
end
end
end
end