diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2019-09-09 19:43:15 -0700 |
---|---|---|
committer | Lamont Granquist <lamont@scriptkiddie.org> | 2019-09-10 12:47:38 -0700 |
commit | 5ad3d87a29e6986c1026ec44f64e64bcdb7a2bcd (patch) | |
tree | fe8783dcf60c6f39352f7176dc959ecca6f55d19 | |
parent | b1a5a4f7e3eb30d30ebd1c5b9e22b299441350a1 (diff) | |
download | chef-5ad3d87a29e6986c1026ec44f64e64bcdb7a2bcd.tar.gz |
savegame
Signed-off-by: Lamont Granquist <lamont@scriptkiddie.org>
-rwxr-xr-x | chef-bin/bin/chef-solo | 2 | ||||
-rw-r--r-- | lib/chef/application/base.rb | 107 | ||||
-rw-r--r-- | lib/chef/application/client.rb | 104 | ||||
-rw-r--r-- | lib/chef/application/solo.rb | 85 | ||||
-rw-r--r-- | lib/chef/daemon.rb | 2 | ||||
-rw-r--r-- | lib/chef/deprecated.rb | 6 | ||||
-rw-r--r-- | spec/support/shared/unit/application_dot_d.rb | 2 | ||||
-rw-r--r-- | spec/unit/application/solo_spec.rb | 246 |
8 files changed, 243 insertions, 311 deletions
diff --git a/chef-bin/bin/chef-solo b/chef-bin/bin/chef-solo index 7a2168230d..0847473100 100755 --- a/chef-bin/bin/chef-solo +++ b/chef-bin/bin/chef-solo @@ -21,4 +21,4 @@ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib")) require "chef/application/solo" -Chef::Application::Solo.new.run(enforce_license: true) +Chef::Application::Client.new(solo: true).run diff --git a/lib/chef/application/base.rb b/lib/chef/application/base.rb index c5bff9874e..bef048287c 100644 --- a/lib/chef/application/base.rb +++ b/lib/chef/application/base.rb @@ -45,6 +45,9 @@ class Chef::Application::Base < Chef::Application # Mimic self_pipe sleep from Unicorn to capture signals safely SELF_PIPE = [] # rubocop:disable Style/MutableConstant + # @return <Boolean> If we have been called from chef-solo + attr_reader :solo_flag + option :config_option, long: "--config-option OPTION=VALUE", description: "Override a single configuration option.", @@ -295,6 +298,110 @@ class Chef::Application::Base < Chef::Application attr_reader :chef_client_json + def reconfigure + super + + if Chef::Config[:local_mode] || solo_flag || Chef::Config[:solo_legacy_mode] + Chef::Config[:solo] = true + unless Chef::Config[:solo_legacy_mode] + Chef::Config.local_mode = true + end + end + + # Load all config files in client.d + if Chef::Config[:solo] + load_dot_d(Chef::Config[:solo_d_dir]) if Chef::Config[:solo_d_dir] + else + load_dot_d(Chef::Config[:client_d_dir]) if Chef::Config[:client_d_dir] + end + + set_specific_recipes + + Chef::Config[:fips] = config[:fips] if config.key? :fips + + raise Chef::Exceptions::PIDFileLockfileMatch if Chef::Util::PathHelper.paths_eql? (Chef::Config[:pid_file] || "" ), (Chef::Config[:lockfile] || "") + + Chef::Config[:chef_server_url] = config[:chef_server_url] if config.key?(:chef_server_url) + + if Chef::Config.local_mode + if Chef::Config.key?(:chef_repo_path) && Chef::Config.chef_repo_path.nil? + Chef::Config.delete(:chef_repo_path) + Chef::Log.warn "chef_repo_path was set in a config file but was empty. Assuming #{Chef::Config.chef_repo_path}" + end + + if Chef::Config.local_mode && !Chef::Config.key?(:cookbook_path) && !Chef::Config.key?(:chef_repo_path) + Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd) + end + end + + if Chef::Config[:recipe_url] + if !Chef::Config.solo + Chef::Application.fatal!("recipe-url can be used only in local-mode") + else + if Chef::Config[:delete_entire_chef_repo] + Chef::Log.trace "Cleanup path #{Chef::Config.chef_repo_path} before extract recipes into it" + FileUtils.rm_rf(Chef::Config.chef_repo_path, secure: true) + end + Chef::Log.trace "Creating path #{Chef::Config.chef_repo_path} to extract recipes into" + FileUtils.mkdir_p(Chef::Config.chef_repo_path) + tarball_path = File.join(Chef::Config.chef_repo_path, "recipes.tgz") + fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path) + Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/) + config_path = File.join(Chef::Config.chef_repo_path, "#{Chef::Dist::USER_CONF_DIR}/config.rb") + Chef::Config.from_string(IO.read(config_path), config_path) if File.file?(config_path) + end + end + + Chef::Config.chef_zero.host = config[:chef_zero_host] if config[:chef_zero_host] + Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port] + + if config[:target] || Chef::Config.target + Chef::Config.target_mode.enabled = true + Chef::Config.target_mode.host = config[:target] || Chef::Config.target + Chef::Config.node_name = Chef::Config.target_mode.host unless Chef::Config.node_name + end + + if Chef::Config[:daemonize] + Chef::Config[:interval] ||= 1800 + end + + if Chef::Config[:once] + Chef::Config[:interval] = nil + Chef::Config[:splay] = nil + end + + # supervisor processes are enabled by default for interval-running processes but not for one-shot runs + if Chef::Config[:client_fork].nil? + Chef::Config[:client_fork] = !!Chef::Config[:interval] + end + + if Chef::Config[:interval] + if Chef::Platform.windows? + Chef::Application.fatal!(windows_interval_error_message) + elsif !Chef::Config[:client_fork] + Chef::Application.fatal!(unforked_interval_error_message) + end + end + + if Chef::Config[:json_attribs] + config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) + @chef_client_json = config_fetcher.fetch_json + end + end + + def load_config_file + if !config.key?(:config_file) && !config[:disable_config] + if config[:local_mode] + config[:config_file] = Chef::WorkstationConfigLoader.new(nil, Chef::Log).config_location + else + config[:config_file] = Chef::Config.platform_specific_path("#{Chef::Dist::CONF_DIR}/client.rb") + end + end + + # Load the client.rb configuration + super + end + def setup_application Chef::Daemon.change_privilege end diff --git a/lib/chef/application/client.rb b/lib/chef/application/client.rb index 890ecbd385..2ea3df9f7b 100644 --- a/lib/chef/application/client.rb +++ b/lib/chef/application/client.rb @@ -63,99 +63,23 @@ class Chef::Application::Client < Chef::Application::Base long: "--recipe-url=RECIPE_URL", description: "Pull down a remote archive of recipes and unpack it to the cookbook cache. Only used in local mode." - # Reconfigure the chef client - # Re-open the JSON attributes and load them into the node - def reconfigure - super - - raise Chef::Exceptions::PIDFileLockfileMatch if Chef::Util::PathHelper.paths_eql? (Chef::Config[:pid_file] || "" ), (Chef::Config[:lockfile] || "") - - set_specific_recipes - - Chef::Config[:fips] = config[:fips] if config.key? :fips - - Chef::Config[:chef_server_url] = config[:chef_server_url] if config.key? :chef_server_url - - Chef::Config.local_mode = config[:local_mode] if config.key?(:local_mode) - - if Chef::Config.key?(:chef_repo_path) && Chef::Config.chef_repo_path.nil? - Chef::Config.delete(:chef_repo_path) - Chef::Log.warn "chef_repo_path was set in a config file but was empty. Assuming #{Chef::Config.chef_repo_path}" - end - - if Chef::Config.local_mode && !Chef::Config.key?(:cookbook_path) && !Chef::Config.key?(:chef_repo_path) - Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Dir.pwd) - end - - if Chef::Config[:recipe_url] - if !Chef::Config.local_mode - Chef::Application.fatal!("recipe-url can be used only in local-mode") - else - if Chef::Config[:delete_entire_chef_repo] - Chef::Log.trace "Cleanup path #{Chef::Config.chef_repo_path} before extract recipes into it" - FileUtils.rm_rf(Chef::Config.chef_repo_path, secure: true) - end - Chef::Log.trace "Creating path #{Chef::Config.chef_repo_path} to extract recipes into" - FileUtils.mkdir_p(Chef::Config.chef_repo_path) - tarball_path = File.join(Chef::Config.chef_repo_path, "recipes.tgz") - fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path) - Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/) - config_path = File.join(Chef::Config.chef_repo_path, "#{Chef::Dist::USER_CONF_DIR}/config.rb") - Chef::Config.from_string(IO.read(config_path), config_path) if File.file?(config_path) - end - end - - Chef::Config.chef_zero.host = config[:chef_zero_host] if config[:chef_zero_host] - Chef::Config.chef_zero.port = config[:chef_zero_port] if config[:chef_zero_port] - - if config[:target] || Chef::Config.target - Chef::Config.target_mode.enabled = true - Chef::Config.target_mode.host = config[:target] || Chef::Config.target - Chef::Config.node_name = Chef::Config.target_mode.host unless Chef::Config.node_name - end - - if Chef::Config[:daemonize] - Chef::Config[:interval] ||= 1800 - end - - if Chef::Config[:once] - Chef::Config[:interval] = nil - Chef::Config[:splay] = nil - end - - # supervisor processes are enabled by default for interval-running processes but not for one-shot runs - if Chef::Config[:client_fork].nil? - Chef::Config[:client_fork] = !!Chef::Config[:interval] - end - - if Chef::Config[:interval] - if Chef::Platform.windows? - Chef::Application.fatal!(windows_interval_error_message) - elsif !Chef::Config[:client_fork] - Chef::Application.fatal!(unforked_interval_error_message) - end - end - - if Chef::Config[:json_attribs] - config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) - @chef_client_json = config_fetcher.fetch_json - end + def initialize(solo: false) + @solo_flag = solo + super() end - def load_config_file - if !config.key?(:config_file) && !config[:disable_config] - if config[:local_mode] - config[:config_file] = Chef::WorkstationConfigLoader.new(nil, Chef::Log).config_location - else - config[:config_file] = Chef::Config.platform_specific_path("#{Chef::Dist::CONF_DIR}/client.rb") - end + def run(enforce_license: false) + setup_signal_handlers + reconfigure + # setup_application does a Dir.chdir("/") and cannot come before reconfigure or many things break + setup_application + check_license_acceptance if enforce_license + for_ezra if Chef::Config[:ez] + if Chef::Config[:solo_legacy_mode] + Chef::Application::Solo.new.run # FIXME: minimally we just need to reparse the cli and then run_application + else + run_application end - - # Load the client.rb configuration - super - - # Load all config files in client.d - load_dot_d(Chef::Config[:client_d_dir]) if Chef::Config[:client_d_dir] end def configure_logging diff --git a/lib/chef/application/solo.rb b/lib/chef/application/solo.rb index da9ec7f566..0d0b6dd216 100644 --- a/lib/chef/application/solo.rb +++ b/lib/chef/application/solo.rb @@ -49,88 +49,13 @@ class Chef::Application::Solo < Chef::Application::Base long: "--recipe-url RECIPE_URL", description: "Pull down a remote gzipped tarball of recipes and untar it to the cookbook cache." - # Get this party started - def run(enforce_license: false) - setup_signal_handlers - setup_application - reconfigure - check_license_acceptance if enforce_license - for_ezra if Chef::Config[:ez] - if !Chef::Config[:solo_legacy_mode] - Chef::Application::Client.new.run - else - run_application - end + def initialize(solo: true) + @solo_flag = solo + super() end - def reconfigure + def run super - - load_dot_d(Chef::Config[:solo_d_dir]) if Chef::Config[:solo_d_dir] - - set_specific_recipes - - Chef::Config[:fips] = config[:fips] if config.key? :fips - - Chef::Config[:solo] = true - - if !Chef::Config[:solo_legacy_mode] - # Because we re-parse ARGV when we move to chef-client, we need to tidy up some options first. - ARGV.delete("--ez") - - # For back compat reasons, we need to ensure that we try and use the cache_path as a repo first - Chef::Log.trace "Current chef_repo_path is #{Chef::Config.chef_repo_path}" - - if !Chef::Config.key?(:cookbook_path) && !Chef::Config.key?(:chef_repo_path) - Chef::Config.chef_repo_path = Chef::Config.find_chef_repo_path(Chef::Config[:cache_path]) - end - - Chef::Config[:local_mode] = true - Chef::Config[:listen] = false - else - configure_legacy_mode! - end + # Chef.deprecated(:solo_legacy_mode, "Solo legacy mode is deprecated FIXME WORDS"); end - - def configure_legacy_mode! - if Chef::Config[:daemonize] - Chef::Config[:interval] ||= 1800 - end - - # supervisor processes are enabled by default for interval-running processes but not for one-shot runs - if Chef::Config[:client_fork].nil? - Chef::Config[:client_fork] = !!Chef::Config[:interval] - end - - if Chef::Config[:interval] - if Chef::Platform.windows? - Chef::Application.fatal!(windows_interval_error_message) - elsif !Chef::Config[:client_fork] - Chef::Application.fatal!(unforked_interval_error_message) - end - end - - if Chef::Config[:recipe_url] - cookbooks_path = Array(Chef::Config[:cookbook_path]).detect { |e| Pathname.new(e).cleanpath.to_s =~ %r{/cookbooks/*$} } - recipes_path = File.expand_path(File.join(cookbooks_path, "..")) - - if Chef::Config[:delete_entire_chef_repo] - Chef::Log.trace "Cleanup path #{recipes_path} before extract recipes into it" - FileUtils.rm_rf(recipes_path, secure: true) - end - Chef::Log.trace "Creating path #{recipes_path} to extract recipes into" - FileUtils.mkdir_p(recipes_path) - tarball_path = File.join(recipes_path, "recipes.tgz") - fetch_recipe_tarball(Chef::Config[:recipe_url], tarball_path) - Mixlib::Archive.new(tarball_path).extract(Chef::Config.chef_repo_path, perms: false, ignore: /^\.$/) - end - - # json_attribs shuld be fetched after recipe_url tarball is unpacked. - # Otherwise it may fail if points to local file from tarball. - if Chef::Config[:json_attribs] - config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs]) - @chef_client_json = config_fetcher.fetch_json - end - end - end diff --git a/lib/chef/daemon.rb b/lib/chef/daemon.rb index 64c451a26c..d778e5a17d 100644 --- a/lib/chef/daemon.rb +++ b/lib/chef/daemon.rb @@ -1,6 +1,6 @@ # # Author:: AJ Christensen (<aj@junglist.gen.nz>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/lib/chef/deprecated.rb b/lib/chef/deprecated.rb index cda8e197d3..0349a73664 100644 --- a/lib/chef/deprecated.rb +++ b/lib/chef/deprecated.rb @@ -1,5 +1,5 @@ #-- -# Copyright:: Copyright 2016-2018, Chef Software Inc. +# Copyright:: Copyright 2016-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -229,6 +229,10 @@ class Chef target 27 end + class SoloLegacyMode < Base + target 28 + end + class Generic < Base def url "https://docs.chef.io/chef_deprecations_client.html" diff --git a/spec/support/shared/unit/application_dot_d.rb b/spec/support/shared/unit/application_dot_d.rb index 0f2f06eff8..e8ff7c2291 100644 --- a/spec/support/shared/unit/application_dot_d.rb +++ b/spec/support/shared/unit/application_dot_d.rb @@ -1,5 +1,5 @@ # -# Copyright:: Copyright 2016, Chef Software, Inc. +# Copyright:: Copyright 2016-2019, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/spec/unit/application/solo_spec.rb b/spec/unit/application/solo_spec.rb index b70f959ab5..1199482bb7 100644 --- a/spec/unit/application/solo_spec.rb +++ b/spec/unit/application/solo_spec.rb @@ -19,7 +19,7 @@ require "spec_helper" describe Chef::Application::Solo do - let(:app) { Chef::Application::Solo.new } + let(:app) { Chef::Application::Client.new(solo: true) } before do allow(Kernel).to receive(:trap).and_return(:ok) @@ -30,187 +30,159 @@ describe Chef::Application::Solo do allow(app).to receive(:cli_arguments).and_return([]) Chef::Config[:json_attribs] = false - Chef::Config[:solo] = true - Chef::Config[:solo_legacy_mode] = true # protect the unit tests against accidental --delete-entire-chef-repo from firing # for real during tests. DO NOT delete this line. expect(FileUtils).not_to receive(:rm_rf) end - context "in legacy mode" do - describe "configuring the application" do - it "should call set_specific_recipes" do - expect(app).to receive(:set_specific_recipes) - app.reconfigure - end - - it "should set solo mode to true" do - app.reconfigure - expect(Chef::Config[:solo]).to be_truthy - end - - describe "when configured to not fork the client process" do - before do - Chef::Config[:client_fork] = false - Chef::Config[:daemonize] = false - Chef::Config[:interval] = nil - Chef::Config[:splay] = nil - end + describe "configuring the application" do + it "should call set_specific_recipes" do + expect(app).to receive(:set_specific_recipes) + app.reconfigure + end - context "when interval is given" do - before do - Chef::Config[:interval] = 600 - end + it "should set solo mode to true" do + app.reconfigure + expect(Chef::Config[:solo]).to be_truthy + end - it "should terminate with message" do - expect(Chef::Application).to receive(:fatal!).with(/interval runs are (disabled|not supported)/) - app.reconfigure - end - end + describe "when configured to not fork the client process" do + before do + Chef::Config[:client_fork] = false + Chef::Config[:daemonize] = false + Chef::Config[:interval] = nil + Chef::Config[:splay] = nil end - describe "when in daemonized mode and no interval has been set", :unix_only do + context "when interval is given" do before do - Chef::Config[:daemonize] = true + Chef::Config[:interval] = 600 end - it "should set the interval to 1800" do - Chef::Config[:interval] = nil + it "should terminate with message" do + expect(Chef::Application).to receive(:fatal!).with(/interval runs are (disabled|not supported)/) app.reconfigure - expect(Chef::Config[:interval]).to eq(1800) end end + end - describe "when the json_attribs configuration option is specified" do - let(:json_attribs) { { "a" => "b" } } - let(:config_fetcher) { double(Chef::ConfigFetcher, fetch_json: json_attribs) } - let(:json_source) { "https://foo.com/foo.json" } - - before do - Chef::Config[:json_attribs] = json_source - expect(Chef::ConfigFetcher).to receive(:new).with(json_source) - .and_return(config_fetcher) - end - - it "reads the JSON attributes from the specified source" do - app.reconfigure - expect(app.chef_client_json).to eq(json_attribs) - end + describe "when in daemonized mode and no interval has been set", :unix_only do + before do + Chef::Config[:daemonize] = true end - it "downloads a tarball when the recipe_url configuration option is specified" do - Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks" - Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz" - - expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true) - - tarfile = StringIO.new("remote_tarball_content") - target_file = StringIO.new - - expect(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile) - expect(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file) - - archive = double(Mixlib::Archive) - - expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive) - expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ }) + it "should set the interval to 1800" do + Chef::Config[:interval] = nil app.reconfigure - expect(target_file.string).to eq("remote_tarball_content") + expect(Chef::Config[:interval]).to eq(1800) end + end - it "fetches the recipe_url first when both json_attribs and recipe_url are specified" do - json_attribs = { "a" => "b" } - config_fetcher = instance_double("Chef::ConfigFetcher", fetch_json: json_attribs) - - Chef::Config[:json_attribs] = "https://foo.com/foo.json" - Chef::Config[:recipe_url] = "http://icanhas.cheezburger.com/lolcats" - Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks" - expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true) + describe "when the json_attribs configuration option is specified" do + let(:json_attribs) { { "a" => "b" } } + let(:config_fetcher) { double(Chef::ConfigFetcher, fetch_json: json_attribs) } + let(:json_source) { "https://foo.com/foo.json" } - archive = double(Mixlib::Archive) + before do + Chef::Config[:json_attribs] = json_source + expect(Chef::ConfigFetcher).to receive(:new).with(json_source) + .and_return(config_fetcher) + end - expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive) - expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ }) - expect(app).to receive(:fetch_recipe_tarball).ordered - expect(Chef::ConfigFetcher).to receive(:new).ordered.and_return(config_fetcher) + it "reads the JSON attributes from the specified source" do app.reconfigure + expect(app.chef_client_json).to eq(json_attribs) end end - describe "after the application has been configured" do - before do - Chef::Config[:solo] = true - Chef::Config[:solo_legacy_mode] = true - - allow(Chef::Daemon).to receive(:change_privilege) - chef_client = double("Chef::Client") - allow(Chef::Client).to receive(:new).and_return(chef_client) - # this is all stuff the reconfigure method needs - allow(app).to receive(:configure_opt_parser).and_return(true) - allow(app).to receive(:configure_chef).and_return(true) - allow(app).to receive(:configure_logging).and_return(true) - end + it "downloads a tarball when the recipe_url configuration option is specified" do + Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks" + Chef::Config[:recipe_url] = "http://junglist.gen.nz/recipes.tgz" - it "should change privileges" do - expect(Chef::Daemon).to receive(:change_privilege).and_return(true) - app.setup_application - end - end + expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true) - it_behaves_like "an application that loads a dot d" do - let(:dot_d_config_name) { :solo_d_dir } - end - end + tarfile = StringIO.new("remote_tarball_content") + target_file = StringIO.new - context "in local mode" do - before do - Chef::Config[:solo_legacy_mode] = false - end + expect(app).to receive(:open).with("http://junglist.gen.nz/recipes.tgz").and_yield(tarfile) + expect(File).to receive(:open).with("#{Dir.tmpdir}/chef-solo/recipes.tgz", "wb").and_yield(target_file) - it "sets solo mode to true" do - app.reconfigure - expect(Chef::Config[:solo]).to be_truthy - end + archive = double(Mixlib::Archive) - it "sets local mode to true" do + expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive) + expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ }) app.reconfigure - expect(Chef::Config[:local_mode]).to be_truthy + expect(target_file.string).to eq("remote_tarball_content") end - context "argv gets tidied up" do - before do - @original_argv = ARGV.dup - ARGV.clear - Chef::Config[:treat_deprecation_warnings_as_errors] = false - end + it "fetches the recipe_url first when both json_attribs and recipe_url are specified" do + json_attribs = { "a" => "b" } + config_fetcher = instance_double("Chef::ConfigFetcher", fetch_json: json_attribs) - after do - ARGV.replace(@original_argv) - end + Chef::Config[:json_attribs] = "https://foo.com/foo.json" + Chef::Config[:recipe_url] = "http://icanhas.cheezburger.com/lolcats" + Chef::Config[:cookbook_path] = "#{Dir.tmpdir}/chef-solo/cookbooks" + expect(FileUtils).to receive(:mkdir_p).with("#{Dir.tmpdir}/chef-solo").and_return(true) - it "deletes --ez" do - ARGV << "--ez" - app.reconfigure - expect(ARGV.include?("--ez")).to be_falsey - end - end + archive = double(Mixlib::Archive) - it "sets the repo path" do - expect(Chef::Config).to receive(:find_chef_repo_path).and_return("/var/chef") + expect(Mixlib::Archive).to receive(:new).with("#{Dir.tmpdir}/chef-solo/recipes.tgz").and_return(archive) + expect(archive).to receive(:extract).with("#{Dir.tmpdir}/chef-solo", { perms: false, ignore: /^\.$/ }) + expect(app).to receive(:fetch_recipe_tarball).ordered + expect(Chef::ConfigFetcher).to receive(:new).ordered.and_return(config_fetcher) app.reconfigure - expect(Chef::Config.key?(:chef_repo_path)).to be_truthy - expect(Chef::Config[:chef_repo_path]).to eq ("/var/chef") end + end - it "runs chef-client in local mode" do - allow(app).to receive(:setup_application).and_return(true) - allow(app).to receive(:run_application).and_return(true) + describe "after the application has been configured" do + before do + allow(Chef::Daemon).to receive(:change_privilege) + chef_client = double("Chef::Client") + allow(Chef::Client).to receive(:new).and_return(chef_client) + # this is all stuff the reconfigure method needs + allow(app).to receive(:configure_opt_parser).and_return(true) allow(app).to receive(:configure_chef).and_return(true) allow(app).to receive(:configure_logging).and_return(true) - expect(Chef::Application::Client).to receive_message_chain(:new, :run) - app.run end + it "should change privileges" do + expect(Chef::Daemon).to receive(:change_privilege).and_return(true) + app.setup_application + end + end + + describe "testing configure_chef" do + before do + expect(app).to receive(:configure_chef).and_call_original + end + it_behaves_like "an application that loads a dot d" do + let(:dot_d_config_name) { :solo_d_dir } + end + end + + it "sets solo mode to true" do + app.reconfigure + expect(Chef::Config[:solo]).to be_truthy + end + + it "sets local mode to true" do + app.reconfigure + expect(Chef::Config[:local_mode]).to be_truthy + end + + it "sets the repo path" do + expect(Chef::Config).to receive(:find_chef_repo_path).and_return("/var/chef") + app.reconfigure + expect(Chef::Config.key?(:chef_repo_path)).to be_truthy + expect(Chef::Config[:chef_repo_path]).to eq ("/var/chef") + end + + it "runs chef-client in local mode" 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) + app.run end end |