Splitting the Agent class into Agent and Configurer

Once I went to add runinterval support to the Agent class,
I realized it's really two classes:  One that handles starting,
stopping, running, et al (still called Agent), and one that
handles downloading the catalog, running it, etc. (now
called Configurer).

This commit includes some additional code, but 95% of it is just moving code around.

Signed-off-by: Luke Kanies <luke@madstop.com>
This commit is contained in:
Luke Kanies 2009-01-28 17:11:19 -06:00
Родитель e8be6dcad2
Коммит fc14b81f99
15 изменённых файлов: 725 добавлений и 496 удалений

Просмотреть файл

@ -162,7 +162,8 @@ trap(:INT) do
end
require 'puppet'
require 'puppet/network/client'
require 'puppet/agent'
require 'puppet/configurer'
require 'getoptlong'
options = [
@ -329,13 +330,19 @@ Puppet::SSL::Host.ca_location = :remote
Puppet::Transaction::Report.terminus_class = :rest
Puppet::Resource::Catalog.terminus_class = :rest
Puppet::Resource::Catalog.cache_class = :yaml
Puppet::Node::Facts.terminus_class = :facter
Puppet::Node::Facts.cache_class = :rest
# We need tomake the client either way, we just don't start it
# if --no-client is set.
client = Puppet::Network::Client.master.new(args)
agent = Puppet::Agent.new(Puppet::Configurer)
if options[:enable]
client.enable
agent.enable
elsif options[:disable]
client.disable
agent.disable
end
if options[:enable] or options[:disable]
@ -347,12 +354,11 @@ server = nil
# It'd be nice to daemonize later, but we have to daemonize before the
# waitforcert happens.
if Puppet[:daemonize]
client.daemonize
agent.daemonize
end
host = Puppet::SSL::Host.new
cert = host.wait_for_cert(options[:waitforcert])
client.recycle_connection
objects = []
@ -402,7 +408,7 @@ elsif options[:onetime] and Puppet[:listen]
end
if options[:client]
objects << client
objects << agent
end
# Set traps for INT and TERM
@ -417,10 +423,10 @@ if options[:onetime]
end
# Add the service, so the traps work correctly.
Puppet.newservice(client)
Puppet.newservice(agent)
begin
client.run
agent.run
rescue => detail
if Puppet[:trace]
puts detail.backtrace
@ -435,7 +441,7 @@ else
if options[:client]
Puppet.notice "Starting Puppet client version %s" % [Puppet.version]
Puppet.newservice(client)
Puppet.newservice(agent)
end
Puppet.settraps

Просмотреть файл

@ -1,201 +1,74 @@
# The client for interacting with the puppetmaster config server.
require 'sync'
require 'timeout'
require 'puppet/network/http_pool'
require 'puppet/util'
require 'puppet/daemon'
# A general class for triggering a run of another
# class.
class Puppet::Agent
require 'puppet/agent/fact_handler'
require 'puppet/agent/plugin_handler'
require 'puppet/agent/locker'
include Puppet::Daemon
include Puppet::Agent::FactHandler
include Puppet::Agent::PluginHandler
require 'puppet/agent/locker'
include Puppet::Agent::Locker
# For benchmarking
include Puppet::Util
attr_reader :client_class, :client
attr_accessor :stopping
unless defined? @@sync
@@sync = Sync.new
end
attr_accessor :catalog
attr_reader :compile_time
class << self
# Puppetd should only have one instance running, and we need a way
# to retrieve it.
attr_accessor :instance
include Puppet::Util
end
def clear
@catalog.clear(true) if @catalog
@catalog = nil
end
# Initialize and load storage
def dostorage
begin
Puppet::Util::Storage.load
@compile_time ||= Puppet::Util::Storage.cache(:configuration)[:compile_time]
rescue => detail
if Puppet[:trace]
puts detail.backtrace
end
Puppet.err "Corrupt state file %s: %s" % [Puppet[:statefile], detail]
begin
::File.unlink(Puppet[:statefile])
retry
rescue => detail
raise Puppet::Error.new("Cannot remove %s: %s" %
[Puppet[:statefile], detail])
end
end
end
# Just so we can specify that we are "the" instance.
def initialize
Puppet.settings.use(:main, :ssl, :puppetd)
self.class.instance = self
@running = false
def initialize(client_class)
@splayed = false
@client_class = client_class
end
# Prepare for catalog retrieval. Downloads everything necessary, etc.
def prepare
dostorage()
download_plugins()
download_fact_plugins()
upload_facts()
def lockfile_path
client_class.lockfile_path
end
# Mark that we should restart. The Puppet module checks whether we're running,
# so this only gets called if we're in the middle of a run.
def restart
# If we're currently running, then just mark for later
Puppet.notice "Received signal to restart; waiting until run is complete"
@restart = true
end
# Should we restart?
def restart?
if defined? @restart
@restart
else
false
# Perform a run with our client.
def run
if client
Puppet.notice "Run of %s already in progress; skipping" % client_class
return
end
end
# Get the remote catalog, yo. Returns nil if no catalog can be found.
def retrieve_catalog
name = Facter.value("hostname")
catalog_class = Puppet::Resource::Catalog
# First try it with no cache, then with the cache.
result = nil
begin
duration = thinmark do
result = catalog_class.find(name, :use_cache => false)
end
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not retrieve catalog from remote server: %s" % detail
if stopping?
Puppet.notice "In shutdown progress; skipping run"
return
end
unless result
splay
with_client do |client|
begin
duration = thinmark do
result = catalog_class.find(name, :use_cache => true)
end
sync.synchronize { lock { client.run } }
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not retrieve catalog from cache: %s" % detail
Puppet.err "Could not run %s: %s" % [client_class, detail]
end
end
end
def shutdown
if self.stopping?
Puppet.notice "Already in shutdown"
return
end
self.stopping = true
if client and client.respond_to?(:stop)
begin
client.stop
rescue
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not stop %s: %s" % [client_class, detail]
end
end
return nil unless result
convert_catalog(result, duration)
super
ensure
self.stopping = false
end
# Convert a plain resource catalog into our full host catalog.
def convert_catalog(result, duration)
catalog = result.to_ral
catalog.retrieval_duration = duration
catalog.host_config = true
catalog.write_class_file
return catalog
end
# The code that actually runs the catalog.
# This just passes any options on to the catalog,
# which accepts :tags and :ignoreschedules.
def run(options = {})
got_lock = false
splay
Puppet::Util.sync(:puppetrun).synchronize(Sync::EX) do
got_lock = lock do
unless catalog = retrieve_catalog
Puppet.err "Could not retrieve catalog; skipping run"
return
end
begin
benchmark(:notice, "Finished catalog run") do
catalog.apply(options)
end
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Failed to apply catalog: %s" % detail
end
end
unless got_lock
Puppet.notice "Lock file %s exists; skipping catalog run" % lockfile.lockfile
return
end
# Now close all of our existing http connections, since there's no
# reason to leave them lying open.
Puppet::Network::HttpPool.clear_http_instances
lockfile.unlock
# Did we get HUPped during the run? If so, then restart now that we're
# done with the run.
Process.kill(:HUP, $$) if self.restart?
end
end
def running?
lockfile.locked?
end
private
def self.timeout
timeout = Puppet[:configtimeout]
case timeout
when String:
if timeout =~ /^\d+$/
timeout = Integer(timeout)
else
raise ArgumentError, "Configuration timeout must be an integer"
end
when Integer: # nothing
else
raise ArgumentError, "Configuration timeout must be an integer"
end
return timeout
def stopping?
stopping
end
# Have we splayed already?
def splayed?
@splayed
end
@ -210,4 +83,44 @@ class Puppet::Agent
sleep(time)
@splayed = true
end
# Start listening for events. We're pretty much just listening for
# timer events here.
def start
# Create our timer. Puppet will handle observing it and such.
Puppet.newtimer(
:interval => Puppet[:runinterval],
:tolerance => 1,
:start? => true
) do
run()
end
# Run once before we start following the timer
run()
end
def sync
unless defined?(@sync) and @sync
@sync = Sync.new
end
@sync
end
private
# Create and yield a client instance, keeping a reference
# to it during the yield.
def with_client
begin
@client = client_class.new
rescue => details
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not create instance of %s: %s" % [client_class, details]
return
end
yield @client
ensure
@client = nil
end
end

Просмотреть файл

@ -30,9 +30,14 @@ module Puppet::Agent::Locker
def lockfile
unless defined?(@lockfile)
@lockfile = Puppet::Util::Pidlock.new(Puppet[:puppetdlockfile])
#@lockfile = Puppet::Util::Pidlock.new(Puppet[:puppetdlockfile])
@lockfile = Puppet::Util::Pidlock.new(lockfile_path)
end
@lockfile
end
def running?
lockfile.locked?
end
end

185
lib/puppet/configurer.rb Normal file
Просмотреть файл

@ -0,0 +1,185 @@
# The client for interacting with the puppetmaster config server.
require 'sync'
require 'timeout'
require 'puppet/network/http_pool'
require 'puppet/util'
class Puppet::Configurer
require 'puppet/configurer/fact_handler'
require 'puppet/configurer/plugin_handler'
include Puppet::Configurer::FactHandler
include Puppet::Configurer::PluginHandler
# For benchmarking
include Puppet::Util
attr_accessor :catalog
attr_reader :compile_time
# Provide more helpful strings to the logging that the Agent does
def self.to_s
"Puppet configuration client"
end
class << self
# Puppetd should only have one instance running, and we need a way
# to retrieve it.
attr_accessor :instance
include Puppet::Util
end
# How to lock instances of this class.
def self.lockfile_path
Puppet[:puppetdlockfile]
end
def clear
@catalog.clear(true) if @catalog
@catalog = nil
end
# Initialize and load storage
def dostorage
begin
Puppet::Util::Storage.load
@compile_time ||= Puppet::Util::Storage.cache(:configuration)[:compile_time]
rescue => detail
if Puppet[:trace]
puts detail.backtrace
end
Puppet.err "Corrupt state file %s: %s" % [Puppet[:statefile], detail]
begin
::File.unlink(Puppet[:statefile])
retry
rescue => detail
raise Puppet::Error.new("Cannot remove %s: %s" %
[Puppet[:statefile], detail])
end
end
end
# Just so we can specify that we are "the" instance.
def initialize
Puppet.settings.use(:main, :ssl, :puppetd)
self.class.instance = self
@running = false
@splayed = false
end
# Prepare for catalog retrieval. Downloads everything necessary, etc.
def prepare
dostorage()
download_plugins()
download_fact_plugins()
upload_facts()
end
# Mark that we should restart. The Puppet module checks whether we're running,
# so this only gets called if we're in the middle of a run.
def restart
# If we're currently running, then just mark for later
Puppet.notice "Received signal to restart; waiting until run is complete"
@restart = true
end
# Should we restart?
def restart?
if defined? @restart
@restart
else
false
end
end
# Get the remote catalog, yo. Returns nil if no catalog can be found.
def retrieve_catalog
name = Facter.value("hostname")
catalog_class = Puppet::Resource::Catalog
# First try it with no cache, then with the cache.
result = nil
begin
duration = thinmark do
result = catalog_class.find(name, :use_cache => false)
end
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not retrieve catalog from remote server: %s" % detail
end
unless result
begin
duration = thinmark do
result = catalog_class.find(name, :use_cache => true)
end
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Could not retrieve catalog from cache: %s" % detail
end
end
return nil unless result
convert_catalog(result, duration)
end
# Convert a plain resource catalog into our full host catalog.
def convert_catalog(result, duration)
catalog = result.to_ral
catalog.retrieval_duration = duration
catalog.host_config = true
catalog.write_class_file
return catalog
end
# The code that actually runs the catalog.
# This just passes any options on to the catalog,
# which accepts :tags and :ignoreschedules.
def run(options = {})
unless catalog = retrieve_catalog
Puppet.err "Could not retrieve catalog; skipping run"
return
end
begin
benchmark(:notice, "Finished catalog run") do
catalog.apply(options)
end
rescue => detail
puts detail.backtrace if Puppet[:trace]
Puppet.err "Failed to apply catalog: %s" % detail
end
# Now close all of our existing http connections, since there's no
# reason to leave them lying open.
Puppet::Network::HttpPool.clear_http_instances
# Did we get HUPped during the run? If so, then restart now that we're
# done with the run.
Process.kill(:HUP, $$) if self.restart?
end
private
def self.timeout
timeout = Puppet[:configtimeout]
case timeout
when String:
if timeout =~ /^\d+$/
timeout = Integer(timeout)
else
raise ArgumentError, "Configuration timeout must be an integer"
end
when Integer: # nothing
else
raise ArgumentError, "Configuration timeout must be an integer"
end
return timeout
end
end

Просмотреть файл

@ -1,7 +1,7 @@
require 'puppet/agent'
require 'puppet/resource/catalog'
class Puppet::Agent::Downloader
class Puppet::Configurer::Downloader
attr_reader :name, :path, :source, :ignore
# Determine the timeout value to use.

Просмотреть файл

@ -3,7 +3,7 @@ require 'puppet/indirector/facts/facter'
# Break out the code related to facts. This module is
# just included into the agent, but having it here makes it
# easier to test.
module Puppet::Agent::FactHandler
module Puppet::Configurer::FactHandler
def download_fact_plugins?
Puppet[:factsync]
end
@ -23,7 +23,7 @@ module Puppet::Agent::FactHandler
def download_fact_plugins
return unless download_fact_plugins?
Puppet::Agent::Downloader.new("fact", Puppet[:factsource], Puppet[:factdest], Puppet[:factsignore]).evaluate
Puppet::Configurer::Downloader.new("fact", Puppet[:factsource], Puppet[:factdest], Puppet[:factsignore]).evaluate
end
# Clear out all of the loaded facts and reload them from disk.

Просмотреть файл

@ -1,7 +1,7 @@
# Break out the code related to plugins. This module is
# just included into the agent, but having it here makes it
# easier to test.
module Puppet::Agent::PluginHandler
module Puppet::Configurer::PluginHandler
def download_plugins?
Puppet[:pluginsync]
end
@ -9,7 +9,7 @@ module Puppet::Agent::PluginHandler
# Retrieve facts from the central server.
def download_plugins
return nil unless download_plugins?
Puppet::Agent::Downloader.new("plugin", Puppet[:pluginsource], Puppet[:plugindest], Puppet[:pluginsignore]).evaluate.each { |file| load_plugin(file) }
Puppet::Configurer::Downloader.new("plugin", Puppet[:pluginsource], Puppet[:plugindest], Puppet[:pluginsignore]).evaluate.each { |file| load_plugin(file) }
end
def load_plugin(file)

Просмотреть файл

@ -76,8 +76,6 @@ module Puppet::Daemon
Puppet::Util::Log.destinations.reject { |d| d == :console }.each do |dest|
Puppet::Util::Log.close(dest)
end
super
end
end

Просмотреть файл

@ -6,255 +6,200 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'puppet/agent'
class AgentTestClient
def run
# no-op
end
def stop
# no-op
end
end
describe Puppet::Agent do
it "should include the Plugin Handler module" do
Puppet::Agent.ancestors.should be_include(Puppet::Agent::PluginHandler)
before do
@agent = Puppet::Agent.new(AgentTestClient)
# So we don't actually try to hit the filesystem.
@agent.stubs(:lock).yields
end
it "should include the Fact Handler module" do
Puppet::Agent.ancestors.should be_include(Puppet::Agent::FactHandler)
it "should set its client class at initialization" do
Puppet::Agent.new("foo").client_class.should == "foo"
end
it "should include the Locker module" do
Puppet::Agent.ancestors.should be_include(Puppet::Agent::Locker)
end
end
describe Puppet::Agent, "when executing a catalog run" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Agent.new
@agent.stubs(:splay)
@agent.stubs(:lock).yields.then.returns true
end
it "should create an instance of its client class and run it when asked to run" do
client = mock 'client'
AgentTestClient.expects(:new).returns client
it "should splay" do
Puppet::Util.sync(:puppetrun).stubs(:synchronize)
@agent.expects(:splay)
@agent.run
end
it "should use a global mutex to make sure no other thread is executing the catalog" do
sync = mock 'sync'
Puppet::Util.expects(:sync).with(:puppetrun).returns sync
sync.expects(:synchronize)
@agent.expects(:retrieve_config).never # i.e., if we don't yield, we don't retrieve the config
@agent.run
end
it "should retrieve the catalog if a lock is attained" do
@agent.expects(:lock).yields.then.returns true
@agent.expects(:retrieve_catalog)
client.expects(:run)
@agent.run
end
it "should log and do nothing if the lock cannot be acquired" do
@agent.expects(:lock).returns false
@agent.expects(:retrieve_catalog).never
Puppet.expects(:notice)
@agent.run
it "should determine its lock file path by asking the client class" do
AgentTestClient.expects(:lockfile_path).returns "/my/lock"
@agent.lockfile_path.should == "/my/lock"
end
it "should retrieve the catalog" do
@agent.expects(:retrieve_catalog)
describe "when running" do
it "should splay" do
@agent.expects(:splay)
@agent.run
@agent.run
end
it "should do nothing if a client instance exists" do
@agent.expects(:client).returns "eh"
AgentTestClient.expects(:new).never
@agent.run
end
it "should do nothing if it is in the process of stopping" do
@agent.expects(:stopping?).returns true
AgentTestClient.expects(:new).never
@agent.run
end
it "should not fail if a client class instance cannot be created" do
AgentTestClient.expects(:new).raises "eh"
Puppet.expects(:err)
@agent.run
end
it "should not fail if there is an exception while running its client" do
client = AgentTestClient.new
AgentTestClient.expects(:new).returns client
client.expects(:run).raises "eh"
Puppet.expects(:err)
@agent.run
end
it "should use a mutex to restrict multi-threading" do
client = AgentTestClient.new
AgentTestClient.expects(:new).returns client
mutex = mock 'mutex'
@agent.expects(:sync).returns mutex
mutex.expects(:synchronize)
client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it
@agent.run
end
it "should use a filesystem lock to restrict multiple processes running the agent" do
client = AgentTestClient.new
AgentTestClient.expects(:new).returns client
@agent.expects(:lock)
client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it
@agent.run
end
it "should make its client instance available while running" do
client = AgentTestClient.new
AgentTestClient.expects(:new).returns client
client.expects(:run).with { @agent.client.should equal(client); true }
@agent.run
end
end
it "should log a failure and do nothing if no catalog can be retrieved" do
@agent.expects(:retrieve_catalog).returns nil
describe "when splaying" do
before do
Puppet.settings.stubs(:value).with(:splay).returns true
Puppet.settings.stubs(:value).with(:splaylimit).returns "10"
end
Puppet.expects(:err)
it "should do nothing if splay is disabled" do
Puppet.settings.expects(:value).returns false
@agent.expects(:sleep).never
@agent.splay
end
@agent.run
end
it "should do nothing if it has already splayed" do
@agent.expects(:splayed?).returns true
@agent.expects(:sleep).never
@agent.splay
end
it "should apply the catalog with all options to :run" do
catalog = stub 'catalog', :retrieval_duration= => nil
@agent.expects(:retrieve_catalog).returns catalog
it "should log that it is splaying" do
@agent.stubs :sleep
Puppet.expects :info
@agent.splay
end
catalog.expects(:apply).with(:one => true)
@agent.run :one => true
it "should sleep for a random portion of the splaylimit plus 1" do
Puppet.settings.expects(:value).with(:splaylimit).returns "50"
@agent.expects(:rand).with(51).returns 10
@agent.expects(:sleep).with(10)
@agent.splay
end
it "should mark that it has splayed" do
@agent.stubs(:sleep)
@agent.splay
@agent.should be_splayed
end
end
it "should benchmark how long it takes to apply the catalog" do
@agent.expects(:benchmark).with(:notice, "Finished catalog run")
describe "when shutting down" do
it "should do nothing if already stopping" do
@agent.expects(:stopping?).returns true
@agent.shutdown
end
catalog = stub 'catalog', :retrieval_duration= => nil
@agent.expects(:retrieve_catalog).returns catalog
it "should stop the client if one is available and it responds to 'stop'" do
client = AgentTestClient.new
catalog.expects(:apply).never # because we're not yielding
@agent.run
@agent.stubs(:client).returns client
client.expects(:stop)
@agent.shutdown
end
it "should remove its pid file" do
@agent.expects(:rmpidfile)
@agent.shutdown
end
it "should mark itself as stopping while waiting for the client to stop" do
client = AgentTestClient.new
@agent.stubs(:client).returns client
client.expects(:stop).with { @agent.should be_stopping; true }
@agent.shutdown
end
end
it "should HUP itself if it should be restarted" do
catalog = stub 'catalog', :retrieval_duration= => nil, :apply => nil
@agent.expects(:retrieve_catalog).returns catalog
describe "when starting" do
it "should create a timer with the runinterval, a tolerance of 1, and :start? set to true" do
Puppet.settings.expects(:value).with(:runinterval).returns 5
Puppet.expects(:newtimer).with(:interval => 5, :start? => true, :tolerance => 1)
@agent.stubs(:run)
@agent.start
end
Process.expects(:kill).with(:HUP, $$)
it "should run once immediately" do
Puppet.stubs(:newtimer)
@agent.expects(:run)
@agent.start
end
@agent.expects(:restart?).returns true
it "should run within the block passed to the timer" do
Puppet.stubs(:newtimer).yields
@agent.expects(:run).times(2)
@agent.start
end
@agent.run
end
it "should not HUP itself if it should not be restarted" do
catalog = stub 'catalog', :retrieval_duration= => nil, :apply => nil
@agent.expects(:retrieve_catalog).returns catalog
Process.expects(:kill).never
@agent.expects(:restart?).returns false
@agent.run
end
end
describe Puppet::Agent, "when retrieving a catalog" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Agent.new
@catalog = Puppet::Resource::Catalog.new
@agent.stubs(:convert_catalog).returns @catalog
end
it "should use the Catalog class to get its catalog" do
Puppet::Resource::Catalog.expects(:find).returns @catalog
@agent.retrieve_catalog
end
it "should use its Facter name to retrieve the catalog" do
Facter.stubs(:value).returns "eh"
Facter.expects(:value).with("hostname").returns "myhost"
Puppet::Resource::Catalog.expects(:find).with { |name, options| name == "myhost" }.returns @catalog
@agent.retrieve_catalog
end
it "should default to returning a catalog retrieved directly from the server, skipping the cache" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns @catalog
@agent.retrieve_catalog.should == @catalog
end
it "should return the cached catalog when no catalog can be retrieved from the server" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns nil
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.returns @catalog
@agent.retrieve_catalog.should == @catalog
end
it "should not look in the cache for a catalog if one is returned from the server" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns @catalog
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.never
@agent.retrieve_catalog.should == @catalog
end
it "should return the cached catalog when retrieving the remote catalog throws an exception" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.raises "eh"
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.returns @catalog
@agent.retrieve_catalog.should == @catalog
end
it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns nil
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.returns nil
@agent.retrieve_catalog.should be_nil
end
it "should convert the catalog before returning" do
Puppet::Resource::Catalog.stubs(:find).returns @catalog
@agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog"
@agent.retrieve_catalog.should == "converted catalog"
end
it "should return nil if there is an error while retrieving the catalog" do
Puppet::Resource::Catalog.expects(:find).raises "eh"
@agent.retrieve_catalog.should be_nil
end
end
describe Puppet::Agent, "when converting the catalog" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Agent.new
@catalog = Puppet::Resource::Catalog.new
@oldcatalog = stub 'old_catalog', :to_ral => @catalog
end
it "should convert the catalog to a RAL-formed catalog" do
@oldcatalog.expects(:to_ral).returns @catalog
@agent.convert_catalog(@oldcatalog, 10).should equal(@catalog)
end
it "should record the passed retrieval time with the RAL catalog" do
@catalog.expects(:retrieval_duration=).with 10
@agent.convert_catalog(@oldcatalog, 10)
end
it "should write the RAL catalog's class file" do
@catalog.expects(:write_class_file)
@agent.convert_catalog(@oldcatalog, 10)
end
it "should mark the RAL catalog as a host catalog" do
@catalog.expects(:host_config=).with true
@agent.convert_catalog(@oldcatalog, 10)
end
end
describe Puppet::Agent, "when preparing for a run" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Agent.new
@agent.stubs(:dostorage)
@agent.stubs(:upload_facts)
@facts = {"one" => "two", "three" => "four"}
end
it "should initialize the metadata store" do
@agent.class.stubs(:facts).returns(@facts)
@agent.expects(:dostorage)
@agent.prepare
end
it "should download fact plugins" do
@agent.stubs(:dostorage)
@agent.expects(:download_fact_plugins)
@agent.prepare
end
it "should download plugins" do
@agent.stubs(:dostorage)
@agent.expects(:download_plugins)
@agent.prepare
end
it "should upload facts to use for catalog retrieval" do
@agent.stubs(:dostorage)
@agent.expects(:upload_facts)
@agent.prepare
it "should run within the block passed to the timer" do
Puppet.stubs(:newtimer).yields
@agent.expects(:run).times(2)
@agent.start
end
end
end

Просмотреть файл

@ -11,12 +11,21 @@ end
describe Puppet::Agent::Locker do
before do
@locker = LockerTester.new
@locker.stubs(:lockfile_path).returns "/my/lock"
end
it "should use a Pidlock instance as its lockfile" do
@locker.lockfile.should be_instance_of(Puppet::Util::Pidlock)
end
it "should use 'lockfile_path' to determine its lockfile path" do
@locker.expects(:lockfile_path).returns "/my/lock"
lock = Puppet::Util::Pidlock.new("/my/lock")
Puppet::Util::Pidlock.expects(:new).with("/my/lock").returns lock
@locker.lockfile
end
it "should reuse the same lock file each time" do
@locker.lockfile.should equal(@locker.lockfile)
end
@ -83,4 +92,9 @@ describe Puppet::Agent::Locker do
lambda { @locker.lock { raise "foo" } }.should raise_error(RuntimeError)
end
it "should be considered running if the lockfile is locked" do
@locker.lockfile.expects(:locked?).returns true
@locker.should be_running
end
end

225
spec/unit/configurer.rb Executable file
Просмотреть файл

@ -0,0 +1,225 @@
#!/usr/bin/env ruby
#
# Created by Luke Kanies on 2007-11-12.
# Copyright (c) 2007. All rights reserved.
require File.dirname(__FILE__) + '/../spec_helper'
require 'puppet/configurer'
describe Puppet::Configurer do
it "should include the Plugin Handler module" do
Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::PluginHandler)
end
it "should include the Fact Handler module" do
Puppet::Configurer.ancestors.should be_include(Puppet::Configurer::FactHandler)
end
it "should use the puppetdlockfile as its lockfile path" do
Puppet.settings.expects(:value).with(:puppetdlockfile).returns("/my/lock")
Puppet::Configurer.lockfile_path.should == "/my/lock"
end
end
describe Puppet::Configurer, "when executing a catalog run" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Configurer.new
end
it "should retrieve the catalog" do
@agent.expects(:retrieve_catalog)
@agent.run
end
it "should log a failure and do nothing if no catalog can be retrieved" do
@agent.expects(:retrieve_catalog).returns nil
Puppet.expects(:err)
@agent.run
end
it "should apply the catalog with all options to :run" do
catalog = stub 'catalog', :retrieval_duration= => nil
@agent.expects(:retrieve_catalog).returns catalog
catalog.expects(:apply).with(:one => true)
@agent.run :one => true
end
it "should benchmark how long it takes to apply the catalog" do
@agent.expects(:benchmark).with(:notice, "Finished catalog run")
catalog = stub 'catalog', :retrieval_duration= => nil
@agent.expects(:retrieve_catalog).returns catalog
catalog.expects(:apply).never # because we're not yielding
@agent.run
end
it "should HUP itself if it should be restarted" do
catalog = stub 'catalog', :retrieval_duration= => nil, :apply => nil
@agent.expects(:retrieve_catalog).returns catalog
Process.expects(:kill).with(:HUP, $$)
@agent.expects(:restart?).returns true
@agent.run
end
it "should not HUP itself if it should not be restarted" do
catalog = stub 'catalog', :retrieval_duration= => nil, :apply => nil
@agent.expects(:retrieve_catalog).returns catalog
Process.expects(:kill).never
@agent.expects(:restart?).returns false
@agent.run
end
end
describe Puppet::Configurer, "when retrieving a catalog" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Configurer.new
@catalog = Puppet::Resource::Catalog.new
@agent.stubs(:convert_catalog).returns @catalog
end
it "should use the Catalog class to get its catalog" do
Puppet::Resource::Catalog.expects(:find).returns @catalog
@agent.retrieve_catalog
end
it "should use its Facter name to retrieve the catalog" do
Facter.stubs(:value).returns "eh"
Facter.expects(:value).with("hostname").returns "myhost"
Puppet::Resource::Catalog.expects(:find).with { |name, options| name == "myhost" }.returns @catalog
@agent.retrieve_catalog
end
it "should default to returning a catalog retrieved directly from the server, skipping the cache" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns @catalog
@agent.retrieve_catalog.should == @catalog
end
it "should return the cached catalog when no catalog can be retrieved from the server" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns nil
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.returns @catalog
@agent.retrieve_catalog.should == @catalog
end
it "should not look in the cache for a catalog if one is returned from the server" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns @catalog
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.never
@agent.retrieve_catalog.should == @catalog
end
it "should return the cached catalog when retrieving the remote catalog throws an exception" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.raises "eh"
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.returns @catalog
@agent.retrieve_catalog.should == @catalog
end
it "should return nil if no cached catalog is available and no catalog can be retrieved from the server" do
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == false }.returns nil
Puppet::Resource::Catalog.expects(:find).with { |name, options| options[:use_cache] == true }.returns nil
@agent.retrieve_catalog.should be_nil
end
it "should convert the catalog before returning" do
Puppet::Resource::Catalog.stubs(:find).returns @catalog
@agent.expects(:convert_catalog).with { |cat, dur| cat == @catalog }.returns "converted catalog"
@agent.retrieve_catalog.should == "converted catalog"
end
it "should return nil if there is an error while retrieving the catalog" do
Puppet::Resource::Catalog.expects(:find).raises "eh"
@agent.retrieve_catalog.should be_nil
end
end
describe Puppet::Configurer, "when converting the catalog" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Configurer.new
@catalog = Puppet::Resource::Catalog.new
@oldcatalog = stub 'old_catalog', :to_ral => @catalog
end
it "should convert the catalog to a RAL-formed catalog" do
@oldcatalog.expects(:to_ral).returns @catalog
@agent.convert_catalog(@oldcatalog, 10).should equal(@catalog)
end
it "should record the passed retrieval time with the RAL catalog" do
@catalog.expects(:retrieval_duration=).with 10
@agent.convert_catalog(@oldcatalog, 10)
end
it "should write the RAL catalog's class file" do
@catalog.expects(:write_class_file)
@agent.convert_catalog(@oldcatalog, 10)
end
it "should mark the RAL catalog as a host catalog" do
@catalog.expects(:host_config=).with true
@agent.convert_catalog(@oldcatalog, 10)
end
end
describe Puppet::Configurer, "when preparing for a run" do
before do
Puppet.settings.stubs(:use).returns(true)
@agent = Puppet::Configurer.new
@agent.stubs(:dostorage)
@agent.stubs(:upload_facts)
@facts = {"one" => "two", "three" => "four"}
end
it "should initialize the metadata store" do
@agent.class.stubs(:facts).returns(@facts)
@agent.expects(:dostorage)
@agent.prepare
end
it "should download fact plugins" do
@agent.stubs(:dostorage)
@agent.expects(:download_fact_plugins)
@agent.prepare
end
it "should download plugins" do
@agent.stubs(:dostorage)
@agent.expects(:download_plugins)
@agent.prepare
end
it "should upload facts to use for catalog retrieval" do
@agent.stubs(:dostorage)
@agent.expects(:upload_facts)
@agent.prepare
end
end

Просмотреть файл

@ -2,36 +2,36 @@
require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/agent/downloader'
require 'puppet/configurer/downloader'
describe Puppet::Agent::Downloader do
describe Puppet::Configurer::Downloader do
it "should require a name" do
lambda { Puppet::Agent::Downloader.new }.should raise_error(ArgumentError)
lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError)
end
it "should require a path and a source at initialization" do
lambda { Puppet::Agent::Downloader.new("name") }.should raise_error(ArgumentError)
lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError)
end
it "should set the name, path and source appropriately" do
dler = Puppet::Agent::Downloader.new("facts", "path", "source")
dler = Puppet::Configurer::Downloader.new("facts", "path", "source")
dler.name.should == "facts"
dler.path.should == "path"
dler.source.should == "source"
end
it "should be able to provide a timeout value" do
Puppet::Agent::Downloader.should respond_to(:timeout)
Puppet::Configurer::Downloader.should respond_to(:timeout)
end
it "should use the configtimeout, converted to an integer, as its timeout" do
Puppet.settings.expects(:value).with(:configtimeout).returns "50"
Puppet::Agent::Downloader.timeout.should == 50
Puppet::Configurer::Downloader.timeout.should == 50
end
describe "when creating the file that does the downloading" do
before do
@dler = Puppet::Agent::Downloader.new("foo", "path", "source")
@dler = Puppet::Configurer::Downloader.new("foo", "path", "source")
end
it "should create a file instance with the right path and source" do
@ -83,14 +83,14 @@ describe Puppet::Agent::Downloader do
it "should support providing an 'ignore' parameter" do
Puppet::Type.type(:file).expects(:create).with { |opts| opts[:ignore] == ".svn" }
@dler = Puppet::Agent::Downloader.new("foo", "path", "source", ".svn")
@dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn")
@dler.file
end
end
describe "when creating the catalog to do the downloading" do
before do
@dler = Puppet::Agent::Downloader.new("foo", "path", "source")
@dler = Puppet::Configurer::Downloader.new("foo", "path", "source")
end
it "should create a catalog and add the file to it" do
@ -108,7 +108,7 @@ describe Puppet::Agent::Downloader do
describe "when downloading" do
before do
@dler = Puppet::Agent::Downloader.new("foo", "path", "source")
@dler = Puppet::Configurer::Downloader.new("foo", "path", "source")
end
it "should log that it is downloading" do
@ -119,7 +119,7 @@ describe Puppet::Agent::Downloader do
end
it "should set a timeout for the download" do
Puppet::Agent::Downloader.expects(:timeout).returns 50
Puppet::Configurer::Downloader.expects(:timeout).returns 50
Timeout.expects(:timeout).with(50)
@dler.evaluate

Просмотреть файл

@ -1,14 +1,14 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/agent'
require 'puppet/agent/fact_handler'
require 'puppet/configurer'
require 'puppet/configurer/fact_handler'
class FactHandlerTester
include Puppet::Agent::FactHandler
include Puppet::Configurer::FactHandler
end
describe Puppet::Agent::FactHandler do
describe Puppet::Configurer::FactHandler do
before do
@facthandler = FactHandlerTester.new
end
@ -32,7 +32,7 @@ describe Puppet::Agent::FactHandler do
end
it "should not download fact plugins when downloading is disabled" do
Puppet::Agent::Downloader.expects(:new).never
Puppet::Configurer::Downloader.expects(:new).never
@facthandler.expects(:download_fact_plugins?).returns false
@facthandler.download_fact_plugins
end
@ -44,7 +44,7 @@ describe Puppet::Agent::FactHandler do
Puppet.settings.expects(:value).with(:factdest).returns "fdest"
Puppet.settings.expects(:value).with(:factsignore).returns "fignore"
Puppet::Agent::Downloader.expects(:new).with("fact", "fsource", "fdest", "fignore").returns downloader
Puppet::Configurer::Downloader.expects(:new).with("fact", "fsource", "fdest", "fignore").returns downloader
downloader.expects(:evaluate)

Просмотреть файл

@ -1,14 +1,14 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../spec_helper'
require 'puppet/agent'
require 'puppet/agent/plugin_handler'
require 'puppet/configurer'
require 'puppet/configurer/plugin_handler'
class PluginHandlerTester
include Puppet::Agent::PluginHandler
include Puppet::Configurer::PluginHandler
end
describe Puppet::Agent::PluginHandler do
describe Puppet::Configurer::PluginHandler do
before do
@pluginhandler = PluginHandlerTester.new
end
@ -32,7 +32,7 @@ describe Puppet::Agent::PluginHandler do
end
it "should not download plugins when downloading is disabled" do
Puppet::Agent::Downloader.expects(:new).never
Puppet::Configurer::Downloader.expects(:new).never
@pluginhandler.expects(:download_plugins?).returns false
@pluginhandler.download_plugins
end
@ -44,7 +44,7 @@ describe Puppet::Agent::PluginHandler do
Puppet.settings.expects(:value).with(:plugindest).returns "pdest"
Puppet.settings.expects(:value).with(:pluginsignore).returns "pignore"
Puppet::Agent::Downloader.expects(:new).with("plugin", "psource", "pdest", "pignore").returns downloader
Puppet::Configurer::Downloader.expects(:new).with("plugin", "psource", "pdest", "pignore").returns downloader
downloader.expects(:evaluate).returns []
@ -59,7 +59,7 @@ describe Puppet::Agent::PluginHandler do
it "should load each downloaded file" do
downloader = mock 'downloader'
Puppet::Agent::Downloader.expects(:new).returns downloader
Puppet::Configurer::Downloader.expects(:new).returns downloader
downloader.expects(:evaluate).returns %w{one two}

Просмотреть файл

@ -1,62 +0,0 @@
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/lib/puppettest'
require 'puppettest'
require 'puppet/agent'
require 'mocha'
class TestAgent < Test::Unit::TestCase
include PuppetTest::ServerTest
def setup
super
@agent_class = Puppet::Agent
end
# Make sure we get a value for timeout
def test_config_timeout
master = Puppet::Agent
time = Integer(Puppet[:configtimeout])
assert_equal(time, master.timeout, "Did not get default value for timeout")
assert_equal(time, master.timeout, "Did not get default value for timeout on second run")
# Reset it
Puppet[:configtimeout] = "50"
assert_equal(50, master.timeout, "Did not get changed default value for timeout")
assert_equal(50, master.timeout, "Did not get changed default value for timeout on second run")
# Now try an integer
Puppet[:configtimeout] = 100
assert_equal(100, master.timeout, "Did not get changed integer default value for timeout")
assert_equal(100, master.timeout, "Did not get changed integer default value for timeout on second run")
end
def test_splay
client = Puppet::Agent.new
# Make sure we default to no splay
client.expects(:sleep).never
assert_nothing_raised("Failed to call splay") do
client.send(:splay)
end
# Now set it to true and make sure we get the right value
client = Puppet::Agent.new
client.expects(:sleep)
Puppet[:splay] = true
assert_nothing_raised("Failed to call sleep when splay is true") do
client.send(:splay)
end
# Now try it again
client = Puppet::Agent.new
client.expects(:sleep)
assert_nothing_raised("Failed to call sleep when splay is true with a cached value") do
client.send(:splay)
end
end
end