File Manager
Viewing File: apply.rb
#
# Author:: Bryan W. Berry (<bryan.berry@gmail.com>)
# Author:: Daniel DeLeo (<dan@kallistec.com>)
# Copyright:: Copyright 2012-2016, Bryan W. Berry
# Copyright:: Copyright 2012-2016, Daniel DeLeo
# 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"
require "chef/application"
require "chef/client"
require "chef/config"
require "chef/log"
require "fileutils"
require "tempfile"
require "chef/providers"
require "chef/resources"
class Chef::Application::Apply < Chef::Application
banner "Usage: chef-apply [RECIPE_FILE | -e RECIPE_TEXT | -s] [OPTIONS]"
option :execute,
:short => "-e RECIPE_TEXT",
:long => "--execute RECIPE_TEXT",
:description => "Execute resources supplied in a string",
:proc => nil
option :stdin,
:short => "-s",
:long => "--stdin",
:description => "Execute resources read from STDIN",
:boolean => true
option :json_attribs,
:short => "-j JSON_ATTRIBS",
:long => "--json-attributes JSON_ATTRIBS",
:description => "Load attributes from a JSON file or URL",
:proc => nil
option :force_logger,
:long => "--force-logger",
:description => "Use logger output instead of formatter output",
:boolean => true,
:default => false
option :force_formatter,
:long => "--force-formatter",
:description => "Use formatter output instead of logger output",
:boolean => true,
:default => false
option :formatter,
:short => "-F FORMATTER",
:long => "--format FORMATTER",
:description => "output format to use",
:proc => lambda { |format| Chef::Config.add_formatter(format) }
option :log_level,
:short => "-l LEVEL",
:long => "--log_level LEVEL",
:description => "Set the log level (debug, info, warn, error, fatal)",
:proc => lambda { |l| l.to_sym }
option :help,
:short => "-h",
:long => "--help",
:description => "Show this message",
:on => :tail,
:boolean => true,
:show_options => true,
:exit => 0
option :version,
:short => "-v",
:long => "--version",
:description => "Show chef version",
:boolean => true,
:proc => lambda { |v| puts "Chef: #{::Chef::VERSION}" },
:exit => 0
option :why_run,
:short => "-W",
:long => "--why-run",
:description => "Enable whyrun mode",
:boolean => true
option :profile_ruby,
:long => "--[no-]profile-ruby",
:description => "Dump complete Ruby call graph stack of entire Chef run (expert only)",
:boolean => true,
:default => false
option :color,
:long => "--[no-]color",
:boolean => true,
:default => true,
:description => "Use colored output, defaults to enabled"
option :minimal_ohai,
:long => "--minimal-ohai",
:description => "Only run the bare minimum ohai plugins chef needs to function",
:boolean => true
attr_reader :json_attribs
def initialize
super
end
def reconfigure
parse_options
Chef::Config.merge!(config)
configure_logging
Chef::Config.export_proxies
Chef::Config.init_openssl
parse_json
end
def parse_json
if Chef::Config[:json_attribs]
config_fetcher = Chef::ConfigFetcher.new(Chef::Config[:json_attribs])
@json_attribs = config_fetcher.fetch_json
end
end
def read_recipe_file(file_name)
if file_name.nil?
Chef::Application.fatal!("No recipe file was provided", Chef::Exceptions::RecipeNotFound.new)
else
recipe_path = File.expand_path(file_name)
unless File.exist?(recipe_path)
Chef::Application.fatal!("No file exists at #{recipe_path}", Chef::Exceptions::RecipeNotFound.new)
end
recipe_fh = open(recipe_path)
recipe_text = recipe_fh.read
[recipe_text, recipe_fh]
end
end
def get_recipe_and_run_context
Chef::Config[:solo_legacy_mode] = true
@chef_client = Chef::Client.new(@json_attribs)
@chef_client.run_ohai
@chef_client.load_node
@chef_client.build_node
run_context = if @chef_client.events.nil?
Chef::RunContext.new(@chef_client.node, {})
else
Chef::RunContext.new(@chef_client.node, {}, @chef_client.events)
end
recipe = Chef::Recipe.new("(chef-apply cookbook)", "(chef-apply recipe)", run_context)
[recipe, run_context]
end
# write recipe to temp file, so in case of error,
# user gets error w/ context
def temp_recipe_file
@recipe_fh = Tempfile.open("recipe-temporary-file")
@recipe_fh.write(@recipe_text)
@recipe_fh.rewind
@recipe_filename = @recipe_fh.path
end
def run_chef_recipe
if config[:execute]
@recipe_text = config[:execute]
temp_recipe_file
elsif config[:stdin]
@recipe_text = STDIN.read
temp_recipe_file
else
if !ARGV[0]
puts opt_parser
Chef::Application.exit! "No recipe file provided", Chef::Exceptions::RecipeNotFound.new
end
@recipe_filename = ARGV[0]
@recipe_text, @recipe_fh = read_recipe_file @recipe_filename
end
recipe, run_context = get_recipe_and_run_context
recipe.instance_eval(@recipe_text, @recipe_filename, 1)
runner = Chef::Runner.new(run_context)
begin
runner.converge
ensure
@recipe_fh.close
end
Chef::Platform::Rebooter.reboot_if_needed!(runner)
end
def run_application
parse_options
run_chef_recipe
Chef::Application.exit! "Exiting", 0
rescue SystemExit
raise
rescue Exception => e
Chef::Application.debug_stacktrace(e)
Chef::Application.fatal!("#{e.class}: #{e.message}", e)
end
# Get this party started
def run
reconfigure
run_application
end
end