Skip to content

Commit

Permalink
Add ability to run recipes directly from chef-client command line
Browse files Browse the repository at this point in the history
  • Loading branch information
John Keiser committed Dec 6, 2013
1 parent d85df00 commit 5e87b84
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 6 deletions.
2 changes: 1 addition & 1 deletion chef.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Gem::Specification.new do |s|

s.add_dependency "mime-types", "~> 1.25"
s.add_dependency "mixlib-config", "~> 2.0"
s.add_dependency "mixlib-cli", "~> 1.3"
s.add_dependency "mixlib-cli", "~> 1.4"
s.add_dependency "mixlib-log", "~> 1.3"
s.add_dependency "mixlib-authentication", "~> 1.3"
s.add_dependency "mixlib-shellout", "~> 1.2"
Expand Down
9 changes: 7 additions & 2 deletions lib/chef/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,17 @@ def self.destroy_server_connectivity
end

# Initializes Chef::Client instance and runs it
def run_chef_client
def run_chef_client(specific_recipes = [])
Chef::Application.setup_server_connectivity

override_runlist = config[:override_runlist]
if specific_recipes.size > 0
override_runlist ||= []
end
@chef_client = Chef::Client.new(
@chef_client_json,
:override_runlist => config[:override_runlist]
:override_runlist => config[:override_runlist],
:specific_recipes => specific_recipes
)
@chef_client_json = nil

Expand Down
4 changes: 3 additions & 1 deletion lib/chef/application/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ def initialize
def reconfigure
super

Chef::Config[:specific_recipes] = cli_arguments.map { |file| File.expand_path(file) }

Chef::Config[:chef_server_url] = config[:chef_server_url] if config.has_key? :chef_server_url

Chef::Config.local_mode = config[:local_mode] if config.has_key?(:local_mode)
Expand Down Expand Up @@ -309,7 +311,7 @@ def run_application
Chef::Log.debug("Splay sleep #{splay} seconds")
sleep splay
end
run_chef_client
run_chef_client(Chef::Config[:specific_recipes])
if Chef::Config[:interval]
Chef::Log.debug("Sleeping for #{Chef::Config[:interval]} seconds")
unless SELF_PIPE.empty?
Expand Down
6 changes: 6 additions & 0 deletions lib/chef/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ def initialize(json_attribs=nil, args={})

@events = EventDispatch::Dispatcher.new(*event_handlers)
@override_runlist = args.delete(:override_runlist)
@specific_recipes = args.delete(:specific_recipes)
runlist_override_sanity_check!
end

Expand Down Expand Up @@ -248,6 +249,11 @@ def setup_run_context
run_status.run_context = run_context

run_context.load(@run_list_expansion)
if @specific_recipes
@specific_recipes.each do |recipe_file|
run_context.load_recipe_file(recipe_file)
end
end
assert_cookbook_path_not_empty(run_context)
run_context
end
Expand Down
11 changes: 11 additions & 0 deletions lib/chef/run_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,17 @@ def load_recipe(recipe_name)
end
end

def load_recipe_file(recipe_file)
if !File.exist?(recipe_file)
raise Chef::Exceptions::RecipeNotFound, "could not find recipe file #{recipe_file}"
end

Chef::Log.debug("Loading Recipe File #{recipe_file}")
recipe = Chef::Recipe.new('@recipe_files', recipe_file, self)
recipe.from_file(recipe_file)
recipe
end

# Looks up an attribute file given the +cookbook_name+ and
# +attr_file_name+. Used by DSL::IncludeAttribute
def resolve_attribute(cookbook_name, attr_file_name)
Expand Down
77 changes: 75 additions & 2 deletions spec/integration/client/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,87 @@
it "should complete with success even with a client key" do
file 'config/client.rb', <<EOM
local_mode true
client_key "#{path_to('mykey.pem')}"
cookbook_path "#{path_to('cookbooks')}"
client_key #{path_to('mykey.pem').inspect}
cookbook_path #{path_to('cookbooks').inspect}
EOM

chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "bin")
result = shell_out("chef-client -c \"#{path_to('config/client.rb')}\" -o 'x::default'", :cwd => chef_dir)
result.error!
end

it "should run recipes specified directly on the command line" do
file 'config/client.rb', <<EOM
local_mode true
client_key #{path_to('mykey.pem').inspect}
cookbook_path #{path_to('cookbooks').inspect}
EOM

file 'arbitrary.rb', <<EOM
file #{path_to('tempfile.txt').inspect} do
content '1'
end
EOM

file 'arbitrary2.rb', <<EOM
file #{path_to('tempfile2.txt').inspect} do
content '2'
end
EOM

chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "bin")
result = shell_out("chef-client -c \"#{path_to('config/client.rb')}\" #{path_to('arbitrary.rb')} #{path_to('arbitrary2.rb')}", :cwd => chef_dir)
result.error!

IO.read(path_to('tempfile.txt')).should == '1'
IO.read(path_to('tempfile2.txt')).should == '2'
end

it "should run recipes specified as relative paths directly on the command line" do
file 'config/client.rb', <<EOM
local_mode true
client_key #{path_to('mykey.pem').inspect}
cookbook_path #{path_to('cookbooks').inspect}
EOM

file 'arbitrary.rb', <<EOM
file #{path_to('tempfile.txt').inspect} do
content '1'
end
EOM

chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "bin")
result = shell_out("#{chef_dir}/chef-client -c \"#{path_to('config/client.rb')}\" arbitrary.rb", :cwd => path_to(''))
result.error!

IO.read(path_to('tempfile.txt')).should == '1'
end

it "should run recipes specified directly on the command line AFTER recipes in the run list" do
file 'config/client.rb', <<EOM
local_mode true
client_key #{path_to('mykey.pem').inspect}
cookbook_path #{path_to('cookbooks').inspect}
EOM

file 'cookbooks/x/recipes/constant_definition.rb', <<EOM
class ::Blah
THECONSTANT = '1'
end
EOM
file 'arbitrary.rb', <<EOM
file #{path_to('tempfile.txt').inspect} do
content ::Blah::THECONSTANT
end
EOM

chef_dir = File.join(File.dirname(__FILE__), "..", "..", "..", "bin")
result = shell_out("#{chef_dir}/chef-client -c \"#{path_to('config/client.rb')}\" -o x::constant_definition arbitrary.rb", :cwd => path_to(''))
result.error!

IO.read(path_to('tempfile.txt')).should == '1'
end

end

it "should complete with success when passed the -z flag" do
Expand Down
1 change: 1 addition & 0 deletions spec/unit/application/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
@app.stub!(:configure_opt_parser).and_return(true)
@app.stub!(:configure_chef).and_return(true)
@app.stub!(:configure_logging).and_return(true)
@app.cli_arguments = []
Chef::Config[:interval] = 10

Chef::Config[:once] = false
Expand Down

0 comments on commit 5e87b84

Please sign in to comment.