summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2009-08-01 12:45:55 -0700
committerJames Turnbull <james@lovedthanlost.net>2009-08-02 17:37:27 +1000
commit7e5b56212eef22be381a480dcaf38b33620674dd (patch)
tree1315c26a67e3d84f87830dacab726ea30ce818af
parent97274ad976e3584ae850ad91cc886fae1dcdbbc6 (diff)
downloadpuppet-7e5b56212eef22be381a480dcaf38b33620674dd.tar.gz
puppet-7e5b56212eef22be381a480dcaf38b33620674dd.tar.xz
puppet-7e5b56212eef22be381a480dcaf38b33620674dd.zip
Adding #2477 - puppet can apply provided catalogs
This provides the other half of #2440 - you can compile catalogs into json with puppetmasterd, and now you can take those json catalogs and apply them. This allows you to use whatever mechanism you want to ship the catalogs around. Signed-off-by: Luke Kanies <luke@madstop.com>
-rw-r--r--lib/puppet/application/puppet.rb50
-rw-r--r--lib/puppet/configurer.rb4
-rwxr-xr-xspec/integration/application/puppet.rb30
-rw-r--r--spec/unit/application/puppet.rb130
-rwxr-xr-xspec/unit/configurer.rb8
5 files changed, 187 insertions, 35 deletions
diff --git a/lib/puppet/application/puppet.rb b/lib/puppet/application/puppet.rb
index ee848a503..dd752846c 100644
--- a/lib/puppet/application/puppet.rb
+++ b/lib/puppet/application/puppet.rb
@@ -16,6 +16,10 @@ Puppet::Application.new(:puppet) do
option("--use-nodes")
option("--detailed-exitcodes")
+ option("--apply catalog", "-a catalog") do |arg|
+ options[:catalog] = arg
+ end
+
option("--logdest LOGDEST", "-l") do |arg|
begin
Puppet::Util::Log.newdestination(arg)
@@ -26,7 +30,37 @@ Puppet::Application.new(:puppet) do
end
dispatch do
- return Puppet[:parseonly] ? :parseonly : :main
+ if options[:catalog]
+ :apply
+ elsif Puppet[:parseonly]
+ :parseonly
+ else
+ :main
+ end
+ end
+
+ command(:apply) do
+ require 'puppet/configurer'
+
+ if options[:catalog] == "-"
+ text = $stdin.read
+ else
+ text = File.read(options[:catalog])
+ end
+
+ begin
+ catalog = JSON.parse(text)
+ unless catalog.is_a?(Puppet::Resource::Catalog)
+ catalog = Puppet::Resource::Catalog.json_create(catalog)
+ end
+ rescue => detail
+ raise Puppet::Error, "Could not deserialize catalog from json: %s" % detail
+ end
+
+ catalog = catalog.to_ral
+
+ configurer = Puppet::Configurer.new
+ configurer.run :catalog => catalog
end
command(:parseonly) do
@@ -40,6 +74,13 @@ Puppet::Application.new(:puppet) do
end
command(:main) do
+ # Set our code or file to use.
+ if options[:code] or ARGV.length == 0
+ Puppet[:code] = options[:code] || STDIN.read
+ else
+ Puppet[:manifest] = ARGV.shift
+ end
+
# Collect our facts.
facts = Puppet::Node::Facts.find(Puppet[:certname])
@@ -126,12 +167,5 @@ Puppet::Application.new(:puppet) do
elsif options[:verbose]
Puppet::Util::Log.level = :info
end
-
- # Set our code or file to use.
- if options[:code] or ARGV.length == 0
- Puppet[:code] = options[:code] || STDIN.read
- else
- Puppet[:manifest] = ARGV.shift
- end
end
end
diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb
index 8ec0b0055..81845f5cf 100644
--- a/lib/puppet/configurer.rb
+++ b/lib/puppet/configurer.rb
@@ -130,7 +130,9 @@ class Puppet::Configurer
def run(options = {})
prepare()
- unless catalog = retrieve_catalog
+ if catalog = options[:catalog]
+ options.delete(:catalog)
+ elsif ! catalog = retrieve_catalog
Puppet.err "Could not retrieve catalog; skipping run"
return
end
diff --git a/spec/integration/application/puppet.rb b/spec/integration/application/puppet.rb
new file mode 100755
index 000000000..0047dd5a3
--- /dev/null
+++ b/spec/integration/application/puppet.rb
@@ -0,0 +1,30 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+require 'puppet_spec/files'
+
+require 'puppet/application/puppet'
+
+describe "Puppet" do
+ include PuppetSpec::Files
+
+ it "should be able to apply catalogs provided in a file in json" do
+ file_to_create = tmpfile("json_catalog")
+ catalog = Puppet::Resource::Catalog.new
+ resource = Puppet::Resource.new(:file, file_to_create, :content => "my stuff")
+ catalog.add_resource resource
+
+ manifest = tmpfile("manifest")
+
+ File.open(manifest, "w") { |f| f.print catalog.to_json }
+
+ puppet = Puppet::Application[:puppet]
+ puppet.options[:catalog] = manifest
+
+ puppet.apply
+
+ File.should be_exist(file_to_create)
+ File.read(file_to_create).should == "my stuff"
+ end
+end
diff --git a/spec/unit/application/puppet.rb b/spec/unit/application/puppet.rb
index 9256b00dc..ca1203405 100644
--- a/spec/unit/application/puppet.rb
+++ b/spec/unit/application/puppet.rb
@@ -112,42 +112,25 @@ describe "Puppet" do
lambda { @puppet.run_setup }.should raise_error(SystemExit)
end
- it "should set the code to run from --code" do
- @puppet.options.stubs(:[]).with(:code).returns("code to run")
- Puppet.expects(:[]=).with(:code,"code to run")
-
- @puppet.run_setup
- end
-
- it "should set the code to run from STDIN if no arguments" do
- ARGV.stubs(:length).returns(0)
- STDIN.stubs(:read).returns("code to run")
-
- Puppet.expects(:[]=).with(:code,"code to run")
-
- @puppet.run_setup
- end
-
- it "should set the manifest if some files are passed on command line" do
- ARGV.stubs(:length).returns(1)
- ARGV.stubs(:shift).returns("site.pp")
-
- Puppet.expects(:[]=).with(:manifest,"site.pp")
-
- @puppet.run_setup
- end
-
end
describe "when executing" do
it "should dispatch to parseonly if parseonly is set" do
+ @puppet.stubs(:options).returns({})
Puppet.stubs(:[]).with(:parseonly).returns(true)
@puppet.get_command.should == :parseonly
end
+ it "should dispatch to 'apply' if it was called with 'apply'" do
+ @puppet.options[:catalog] = "foo"
+
+ @puppet.get_command.should == :apply
+ end
+
it "should dispatch to main if parseonly is not set" do
+ @puppet.stubs(:options).returns({})
Puppet.stubs(:[]).with(:parseonly).returns(false)
@puppet.get_command.should == :main
@@ -203,12 +186,39 @@ describe "Puppet" do
@catalog.stubs(:to_ral).returns(@catalog)
Puppet::Resource::Catalog.stubs(:find).returns(@catalog)
+ STDIN.stubs(:read)
+
@transaction = stub_everything 'transaction'
@catalog.stubs(:apply).returns(@transaction)
@puppet.stubs(:exit)
end
+ it "should set the code to run from --code" do
+ @puppet.options.stubs(:[]).with(:code).returns("code to run")
+ Puppet.expects(:[]=).with(:code,"code to run")
+
+ @puppet.main
+ end
+
+ it "should set the code to run from STDIN if no arguments" do
+ ARGV.stubs(:length).returns(0)
+ STDIN.stubs(:read).returns("code to run")
+
+ Puppet.expects(:[]=).with(:code,"code to run")
+
+ @puppet.main
+ end
+
+ it "should set the manifest if some files are passed on command line" do
+ ARGV.stubs(:length).returns(1)
+ ARGV.stubs(:shift).returns("site.pp")
+
+ Puppet.expects(:[]=).with(:manifest,"site.pp")
+
+ @puppet.main
+ end
+
it "should collect the node facts" do
Puppet::Node::Facts.expects(:find).returns(@facts)
@@ -319,6 +329,74 @@ describe "Puppet" do
end
end
- end
+ describe "the 'apply' command" do
+ before do
+ #Puppet::Resource::Catalog.stubs(:json_create).returns Puppet::Resource::Catalog.new
+ JSON.stubs(:parse).returns Puppet::Resource::Catalog.new
+ end
+
+ it "should read the catalog in from disk if a file name is provided" do
+ @puppet.options[:catalog] = "/my/catalog.json"
+
+ File.expects(:read).with("/my/catalog.json").returns "something"
+
+ @puppet.apply
+ end
+
+ it "should read the catalog in from stdin if '-' is provided" do
+ @puppet.options[:catalog] = "-"
+
+ $stdin.expects(:read).returns "something"
+
+ @puppet.apply
+ end
+
+ it "should deserialize the catalog from json" do
+ @puppet.options[:catalog] = "/my/catalog.json"
+
+ File.expects(:read).returns "something"
+ JSON.expects(:parse).with("something").returns Puppet::Resource::Catalog.new
+
+ @puppet.apply
+ end
+
+ it "should fail helpfully if deserializing fails" do
+ @puppet.options[:catalog] = "/my/catalog.json"
+
+ File.expects(:read).returns "something"
+ JSON.expects(:parse).raises ArgumentError
+
+ lambda { @puppet.apply }.should raise_error(Puppet::Error)
+ end
+
+ it "should convert plain data structures into a catalog if deserialization does not do so" do
+ @puppet.options[:catalog] = "/my/catalog.json"
+
+ File.expects(:read).returns "something"
+ JSON.expects(:parse).with("something").returns({:foo => "bar"})
+ Puppet::Resource::Catalog.expects(:json_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new)
+
+ @puppet.apply
+ end
+
+ it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do
+ @puppet.options[:catalog] = "/my/catalog.json"
+
+ File.expects(:read).returns "something"
+
+ catalog = Puppet::Resource::Catalog.new
+ JSON.expects(:parse).returns catalog
+
+ catalog.expects(:to_ral).returns "mycatalog"
+
+ configurer = stub 'configurer'
+ Puppet::Configurer.expects(:new).returns configurer
+
+ configurer.expects(:run).with(:catalog => "mycatalog")
+
+ @puppet.apply
+ end
+ end
+ end
end
diff --git a/spec/unit/configurer.rb b/spec/unit/configurer.rb
index 9cf22c80c..85bdb9dac 100755
--- a/spec/unit/configurer.rb
+++ b/spec/unit/configurer.rb
@@ -57,6 +57,14 @@ describe Puppet::Configurer, "when executing a catalog run" do
@agent.run :one => true
end
+ it "should accept a catalog and use it instead of retrieving a different one" do
+ catalog = stub 'catalog', :retrieval_duration= => nil
+ @agent.expects(:retrieve_catalog).never
+
+ catalog.expects(:apply).with(:one => true)
+ @agent.run :one => true, :catalog => catalog
+ end
+
it "should benchmark how long it takes to apply the catalog" do
@agent.expects(:benchmark).with(:notice, "Finished catalog run")