Committing most of the scheduling stuff. There is still a bit of work to do in terms of how puppetd interacts with scheduling, but the bulk of the work is done.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@847 980ebf18-57e1-0310-9a29-db15c13687c0
This commit is contained in:
Родитель
258114d48b
Коммит
18e8e74a2e
18
bin/puppetd
18
bin/puppetd
|
@ -11,7 +11,7 @@
|
|||
# puppetd [-h|--help] [-V|--version] [-d|--debug] [-v|--verbose]
|
||||
# [--ssldir <cert directory>] [-l|--logdest <syslog|<file>|console>]
|
||||
# [--fqdn <host name>] [-p|--port <port>] [-o|--onetime]
|
||||
# [-s|--server <server>]
|
||||
# [-s|--server <server>] [-i|--ignoreschedules]
|
||||
# [-w|--waitforcert <seconds>] [-c|--confdir <configuration directory>]
|
||||
# [--vardir <var directory>] [--centrallogging]
|
||||
#
|
||||
|
@ -58,7 +58,15 @@
|
|||
# The port to which to connect on the remote server. Currently defaults to 8139.
|
||||
#
|
||||
# onetime::
|
||||
# Run the configuration once, rather than as a long-running daemon.
|
||||
# Run the configuration once, rather than as a long-running daemon. This is
|
||||
# useful for interactively running puppetd.
|
||||
#
|
||||
# schedule::
|
||||
# What schedule Puppet itself should run on. This dictates how often the
|
||||
# entire configuration is retrieved and run. The default is named 'puppet',
|
||||
# and runs every half hour or so. The schedules themselves are defined in the
|
||||
# configuration, which means that on startup puppetd will always retrieve
|
||||
# the configuration and then check to see if it's scheduled to run.
|
||||
#
|
||||
# server::
|
||||
# The remote server from whom to receive the local configuration. Currently
|
||||
|
@ -117,6 +125,7 @@ result = GetoptLong.new(
|
|||
[ "--noop", "-n", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--onetime", "-o", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--port", "-p", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--schedule", "-S", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--server", "-s", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--ssldir", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--verbose", "-v", GetoptLong::NO_ARGUMENT ],
|
||||
|
@ -134,6 +143,7 @@ waitforcert = false
|
|||
onetime = false
|
||||
|
||||
centrallogs = false
|
||||
|
||||
begin
|
||||
result.each { |opt,arg|
|
||||
case opt
|
||||
|
@ -159,6 +169,10 @@ begin
|
|||
Puppet[:logdest] = :console
|
||||
when "--noop"
|
||||
Puppet[:noop] = true
|
||||
when "--schedule"
|
||||
# This is late-binding -- it'll only look up the schedule name
|
||||
# when it needs to run
|
||||
Puppet[:schedule] = arg
|
||||
when "--ssldir"
|
||||
Puppet[:ssldir] = arg
|
||||
when "--fqdn"
|
||||
|
|
|
@ -94,13 +94,16 @@ PUPPETVERSION = '0.11.2'
|
|||
:httplogfile => [:logdir, "http.log"],
|
||||
:masterlog => [:logdir, "puppetmaster.log"],
|
||||
:masterhttplog => [:logdir, "masterhttp.log"],
|
||||
:checksumfile => [:statedir, "checksums"],
|
||||
:statefile => [:statedir, "state.yaml"],
|
||||
:checksumfile => [:statedir, "state.yaml"],
|
||||
:ssldir => [:puppetconf, "ssl"],
|
||||
|
||||
# and finally the simple answers,
|
||||
:server => "puppet",
|
||||
:user => "puppet",
|
||||
:group => "puppet",
|
||||
:schedule => "puppet",
|
||||
:ignoreschedules => false,
|
||||
:rrdgraph => false,
|
||||
:noop => false,
|
||||
:parseonly => false,
|
||||
|
|
|
@ -264,6 +264,11 @@ module Puppet
|
|||
detail
|
||||
rescue => detail
|
||||
Puppet.err "Found a bug: %s" % detail
|
||||
if Puppet[:debug]
|
||||
puts detail.backtrace
|
||||
end
|
||||
ensure
|
||||
Puppet::Storage.store
|
||||
end
|
||||
Puppet::Metric.gather
|
||||
Puppet::Metric.tally
|
||||
|
@ -271,7 +276,6 @@ module Puppet
|
|||
Metric.store
|
||||
Metric.graph
|
||||
end
|
||||
Puppet::Storage.store
|
||||
|
||||
return transaction
|
||||
end
|
||||
|
@ -416,6 +420,9 @@ module Puppet
|
|||
end
|
||||
@objects = nil
|
||||
|
||||
# First create the default scheduling objects
|
||||
Puppet.type(:schedule).mkdefaultschedules
|
||||
|
||||
# Now convert the objects to real Puppet objects
|
||||
@objects = objects.to_type
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module Puppet
|
|||
attr_reader :validater, :munger, :name, :default
|
||||
attr_accessor :ismetaparameter, :element
|
||||
|
||||
# Define the default value for a given state or parameter. This
|
||||
# Define the default value for a given parameter or parameter. This
|
||||
# means that 'nil' is an invalid default value. This defines
|
||||
# the 'default' instance method.
|
||||
def defaultto(value = nil, &block)
|
||||
|
@ -29,7 +29,7 @@ module Puppet
|
|||
# This is how we munge the value. Basically, this is our
|
||||
# opportunity to convert the value from one form into another.
|
||||
def munge(&block)
|
||||
# I need to wrap the unsafe version in begin/rescue statements,
|
||||
# I need to wrap the unsafe version in begin/rescue parameterments,
|
||||
# but if I directly call the block then it gets bound to the
|
||||
# class's context, not the instance's, thus the two methods,
|
||||
# instead of just one.
|
||||
|
@ -107,6 +107,41 @@ module Puppet
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Define a new value for our parameter.
|
||||
def newvalues(*names)
|
||||
@parametervalues ||= []
|
||||
|
||||
names.each { |name|
|
||||
if @parametervalues.include?(name)
|
||||
Puppet.warning "%s already has a value for %s" % [name, name]
|
||||
end
|
||||
@parametervalues << name
|
||||
}
|
||||
end
|
||||
|
||||
def aliasvalue(name, other)
|
||||
@parametervalues ||= []
|
||||
unless @parametervalues.include?(other)
|
||||
raise Puppet::DevError, "Cannot alias nonexistent value %s" % other
|
||||
end
|
||||
|
||||
@aliasvalues ||= {}
|
||||
@aliasvalues[name] = other
|
||||
end
|
||||
|
||||
def alias(name)
|
||||
@aliasvalues[name]
|
||||
end
|
||||
|
||||
# Return the list of valid values.
|
||||
def values
|
||||
@parametervalues ||= []
|
||||
@aliasvalues ||= {}
|
||||
|
||||
#[@aliasvalues.keys, @parametervalues.keys].flatten
|
||||
@parametervalues.dup
|
||||
end
|
||||
end
|
||||
|
||||
# Just a simple method to proxy instance methods to class methods
|
||||
|
@ -177,6 +212,78 @@ module Puppet
|
|||
raise error
|
||||
end
|
||||
|
||||
# Log a message using the parent's log level.
|
||||
def log(msg)
|
||||
unless @parent[:loglevel]
|
||||
p @parent
|
||||
self.devfail "Parent %s has no loglevel" %
|
||||
@parent.name
|
||||
end
|
||||
Puppet::Log.create(
|
||||
:level => @parent[:loglevel],
|
||||
:message => msg,
|
||||
:source => self
|
||||
)
|
||||
end
|
||||
|
||||
# each parameter class must define the name() method, and parameter instances
|
||||
# do not change that name
|
||||
# this implicitly means that a given object can only have one parameter
|
||||
# instance of a given parameter class
|
||||
def name
|
||||
return self.class.name
|
||||
end
|
||||
|
||||
# for testing whether we should actually do anything
|
||||
def noop
|
||||
unless defined? @noop
|
||||
@noop = false
|
||||
end
|
||||
tmp = @noop || self.parent.noop || Puppet[:noop] || false
|
||||
#debug "noop is %s" % tmp
|
||||
return tmp
|
||||
end
|
||||
|
||||
# return the full path to us, for logging and rollback; not currently
|
||||
# used
|
||||
def path
|
||||
return [@parent.path, self.name].join("/")
|
||||
end
|
||||
|
||||
# If the specified value is allowed, then munge appropriately.
|
||||
munge do |value|
|
||||
if self.class.values.empty?
|
||||
# This parameter isn't using defined values to do its work.
|
||||
return value
|
||||
end
|
||||
intern = value.to_s.intern
|
||||
# If it's a valid value, always return it as a symbol.
|
||||
if self.class.values.include?(intern)
|
||||
retval = intern
|
||||
elsif other = self.class.alias(intern)
|
||||
self.info "returning alias %s for %s" % [other, intern]
|
||||
retval = other
|
||||
else
|
||||
retval = value
|
||||
end
|
||||
retval
|
||||
end
|
||||
|
||||
# Verify that the passed value is valid.
|
||||
validate do |value|
|
||||
if self.class.values.empty?
|
||||
# This parameter isn't using defined values to do its work.
|
||||
return
|
||||
end
|
||||
unless value.is_a?(Symbol)
|
||||
value = value.to_s.intern
|
||||
end
|
||||
unless self.class.values.include?(value) or self.class.alias(value)
|
||||
self.fail "Invalid '%s' value '%s'. Valid values are '%s'" %
|
||||
[self.class.name, value, self.class.values.join(", ")]
|
||||
end
|
||||
end
|
||||
|
||||
# This should only be called for parameters, but go ahead and make
|
||||
# it possible to call for states, too.
|
||||
def value
|
||||
|
@ -195,7 +302,7 @@ module Puppet
|
|||
# late-binding (e.g., users might not exist when the value is assigned
|
||||
# but might when it is asked for).
|
||||
def value=(value)
|
||||
# If we're a state, just hand the processing off to the should method.
|
||||
# If we're a parameter, just hand the processing off to the should method.
|
||||
if self.is_a?(Puppet::State)
|
||||
return self.should = value
|
||||
end
|
||||
|
|
|
@ -9,6 +9,18 @@ module Puppet
|
|||
self.class.load
|
||||
end
|
||||
|
||||
# Return a hash that will be stored to disk. It's worth noting
|
||||
# here that we use the object's full path, not just the name/type
|
||||
# combination. At the least, this is useful for those non-isomorphic
|
||||
# types like exec, but it also means that if an object changes locations
|
||||
# in the configuration it will lose its cache.
|
||||
def self.cache(object)
|
||||
unless object.is_a? Puppet::Type
|
||||
raise Puppet::DevFail, "Must pass a Type instance to Storage.cache"
|
||||
end
|
||||
return @@state[object.path] ||= {}
|
||||
end
|
||||
|
||||
def self.clear
|
||||
@@state.clear
|
||||
Storage.init
|
||||
|
@ -23,32 +35,32 @@ module Puppet
|
|||
self.init
|
||||
|
||||
def self.load
|
||||
if Puppet[:checksumfile].nil?
|
||||
if Puppet[:statefile].nil?
|
||||
raise Puppet::DevError, "Somehow the statefile is nil"
|
||||
end
|
||||
|
||||
unless File.exists?(Puppet[:checksumfile])
|
||||
Puppet.info "Statefile %s does not exist" % Puppet[:checksumfile]
|
||||
unless File.exists?(Puppet[:statefile])
|
||||
Puppet.info "Statefile %s does not exist" % Puppet[:statefile]
|
||||
unless defined? @@state and ! @@state.nil?
|
||||
self.init
|
||||
end
|
||||
return
|
||||
end
|
||||
#Puppet.debug "Loading statefile %s" % Puppet[:checksumfile]
|
||||
Puppet::Util.lock(Puppet[:checksumfile]) { |file|
|
||||
#Puppet.debug "Loading statefile %s" % Puppet[:statefile]
|
||||
Puppet::Util.lock(Puppet[:statefile]) { |file|
|
||||
#@@state = Marshal.load(file)
|
||||
begin
|
||||
@@state = YAML.load(file)
|
||||
rescue => detail
|
||||
Puppet.err "Checksumfile %s is corrupt; replacing" %
|
||||
Puppet[:checksumfile]
|
||||
Puppet[:statefile]
|
||||
begin
|
||||
File.rename(Puppet[:checksumfile],
|
||||
Puppet[:checksumfile] + ".bad")
|
||||
File.rename(Puppet[:statefile],
|
||||
Puppet[:statefile] + ".bad")
|
||||
rescue
|
||||
raise Puppet::Error,
|
||||
"Could not rename corrupt %s; remove manually" %
|
||||
Puppet[:checksumfile]
|
||||
Puppet[:statefile]
|
||||
end
|
||||
end
|
||||
}
|
||||
|
@ -60,44 +72,26 @@ module Puppet
|
|||
@@state.inspect
|
||||
end
|
||||
|
||||
def self.state(myclass)
|
||||
unless myclass.is_a? Class
|
||||
myclass = myclass.class
|
||||
end
|
||||
|
||||
@@state[myclass.to_s] ||= {}
|
||||
return @@state[myclass.to_s]
|
||||
end
|
||||
|
||||
def self.store
|
||||
unless FileTest.directory?(File.dirname(Puppet[:checksumfile]))
|
||||
unless FileTest.directory?(File.dirname(Puppet[:statefile]))
|
||||
begin
|
||||
Puppet.recmkdir(File.dirname(Puppet[:checksumfile]))
|
||||
Puppet.recmkdir(File.dirname(Puppet[:statefile]))
|
||||
Puppet.info "Creating state directory %s" %
|
||||
File.dirname(Puppet[:checksumfile])
|
||||
File.dirname(Puppet[:statefile])
|
||||
rescue => detail
|
||||
Puppet.err "Could not create state file: %s" % detail
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
unless FileTest.exist?(Puppet[:checksumfile])
|
||||
Puppet.info "Creating state file %s" % Puppet[:checksumfile]
|
||||
unless FileTest.exist?(Puppet[:statefile])
|
||||
Puppet.info "Creating state file %s" % Puppet[:statefile]
|
||||
end
|
||||
|
||||
Puppet::Util.lock(Puppet[:checksumfile], File::CREAT|File::WRONLY, 0600) { |file|
|
||||
Puppet::Util.lock(
|
||||
Puppet[:statefile], File::CREAT|File::WRONLY, 0600
|
||||
) { |file|
|
||||
file.print YAML.dump(@@state)
|
||||
#file.puts(Marshal::dump(@@state))
|
||||
#File.open(Puppet[:checksumfile], File::CREAT|File::WRONLY, 0600) { |file|
|
||||
# @@state.each { |klass, thash|
|
||||
# thash.each { |key,value|
|
||||
# Puppet.warning "Storing: %s %s %s" %
|
||||
# [klass, key.inspect, value.inspect]
|
||||
# mvalue = Marshal::dump(value)
|
||||
# file.puts([klass,key,mvalue].join(@@splitchar))
|
||||
# }
|
||||
# }
|
||||
#}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,6 +44,7 @@ class Transaction
|
|||
Puppet.debug "Beginning transaction %s with %s changes" %
|
||||
[self.object_id, @changes.length]
|
||||
|
||||
now = Time.now.to_i
|
||||
events = @changes.collect { |change|
|
||||
if change.is_a?(Puppet::StateChange)
|
||||
change.transaction = self
|
||||
|
@ -66,13 +67,19 @@ class Transaction
|
|||
# should do so
|
||||
end
|
||||
|
||||
# This is kinda lame, because it can result in the same
|
||||
# object being modified multiple times, but that's difficult
|
||||
# to avoid as long as we're syncing each state individually.
|
||||
change.state.parent.cache(:synced, now)
|
||||
|
||||
unless events.nil? or (events.is_a?(Array) and events.empty?)
|
||||
change.changed = true
|
||||
end
|
||||
events
|
||||
else
|
||||
puts caller
|
||||
raise Puppet::DevError,
|
||||
"Transactions cannot handle objects of type %s" % child.class
|
||||
"Transactions cannot handle objects of type %s" % change.class
|
||||
end
|
||||
}.flatten.reject { |event|
|
||||
event.nil?
|
||||
|
@ -107,11 +114,16 @@ class Transaction
|
|||
end
|
||||
# change collection is in-band, and message generation is out-of-band
|
||||
# of course, exception raising is also out-of-band
|
||||
@changes = @objects.collect { |child|
|
||||
now = Time.now.to_i
|
||||
@changes = @objects.find_all { |child|
|
||||
child.scheduled?
|
||||
}.collect { |child|
|
||||
# these children are all Puppet::Type instances
|
||||
# not all of the children will return a change, and Containers
|
||||
# return transactions
|
||||
child.evaluate
|
||||
ary = child.evaluate
|
||||
child.cache(:checked, now)
|
||||
ary
|
||||
}.flatten.reject { |child|
|
||||
child.nil? # remove empties
|
||||
}
|
||||
|
|
|
@ -54,23 +54,6 @@ class Type < Puppet::Element
|
|||
|
||||
public
|
||||
|
||||
# these objects are used for mapping type names (e.g., 'file')
|
||||
# to actual object classes; because Type.inherited is
|
||||
# called before the <subclass>.name method is defined, we need
|
||||
# to store each class in an array, and then later actually iterate
|
||||
# across that array and make a map
|
||||
#@@typeary = [self] # so that the allowedmethods stuff works
|
||||
#@@typehash = Hash.new { |hash,key|
|
||||
# if key.is_a?(String)
|
||||
# key = key.intern
|
||||
# end
|
||||
# if hash.include?(key)
|
||||
# hash[key]
|
||||
# else
|
||||
# raise TypeError.new("Object type %s not found" % key)
|
||||
# end
|
||||
#}
|
||||
|
||||
# the Type class attribute accessors
|
||||
class << self
|
||||
attr_reader :name, :states
|
||||
|
@ -463,6 +446,7 @@ class Type < Puppet::Element
|
|||
end
|
||||
param.ismetaparameter
|
||||
param.class_eval(&block)
|
||||
const_set("MetaParam" + name.to_s.capitalize,param)
|
||||
@@metaparams ||= []
|
||||
@@metaparams << param
|
||||
|
||||
|
@ -483,6 +467,7 @@ class Type < Puppet::Element
|
|||
end
|
||||
param.element = self
|
||||
param.class_eval(&block)
|
||||
const_set("Parameter" + name.to_s.capitalize,param)
|
||||
@parameters ||= []
|
||||
@parameters << param
|
||||
|
||||
|
@ -756,8 +741,12 @@ class Type < Puppet::Element
|
|||
else
|
||||
if @states.has_key?(attr)
|
||||
@states.delete(attr)
|
||||
elsif @parameters.has_key?(attr)
|
||||
@parameters.delete(attr)
|
||||
elsif @metaparams.has_key?(attr)
|
||||
@metaparams.delete(attr)
|
||||
else
|
||||
raise Puppet::DevError.new("Undefined state '#{attr}' in #{self}")
|
||||
raise Puppet::DevError.new("Undefined attribute '#{attr}' in #{self}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -809,11 +798,11 @@ class Type < Puppet::Element
|
|||
|
||||
error = type.new(args.join(" "))
|
||||
|
||||
if @line
|
||||
if defined? @line and @line
|
||||
error.line = @line
|
||||
end
|
||||
|
||||
if @file
|
||||
if defined? @file and @file
|
||||
error.file = @file
|
||||
end
|
||||
|
||||
|
@ -1142,8 +1131,8 @@ class Type < Puppet::Element
|
|||
@noop = false
|
||||
# keeping stats for the total number of changes, and how many were
|
||||
# completely sync'ed
|
||||
# this isn't really sufficient either, because it adds lots of special cases
|
||||
# such as failed changes
|
||||
# this isn't really sufficient either, because it adds lots of special
|
||||
# cases such as failed changes
|
||||
# it also doesn't distinguish between changes from the current transaction
|
||||
# vs. changes over the process lifetime
|
||||
@totalchanges = 0
|
||||
|
@ -1180,15 +1169,42 @@ class Type < Puppet::Element
|
|||
hash.delete(:parent)
|
||||
end
|
||||
|
||||
# Convert all args to symbols
|
||||
hash = self.argclean(hash)
|
||||
|
||||
self.class.allattrs.each { |name|
|
||||
# Let's do the name first, because some things need to happen once
|
||||
# we have the name but before anything else
|
||||
|
||||
attrs = self.class.allattrs
|
||||
namevar = self.class.namevar
|
||||
|
||||
if hash.include?(namevar)
|
||||
self[namevar] = hash[namevar]
|
||||
hash.delete(namevar)
|
||||
if attrs.include?(namevar)
|
||||
attrs.delete(namevar)
|
||||
else
|
||||
self.devfail "My namevar isn't a valid attribute...?"
|
||||
end
|
||||
else
|
||||
self.devfail "I was not passed a namevar"
|
||||
end
|
||||
|
||||
# The information to cache to disk. We have to do this after
|
||||
# the name is set because it uses the name and/or path, but before
|
||||
# everything else is set because the states need to be able to
|
||||
# retrieve their stored info.
|
||||
#@cache = Puppet::Storage.cache(self)
|
||||
|
||||
# This is all of our attributes except the namevar.
|
||||
attrs.each { |name|
|
||||
if hash.include?(name)
|
||||
begin
|
||||
self[name] = hash[name]
|
||||
rescue => detail
|
||||
self.devfail(
|
||||
"Could not set %s on %s: %s" % [name, self.class.name, detail]
|
||||
"Could not set %s on %s: %s" %
|
||||
[name, self.class.name, detail]
|
||||
)
|
||||
end
|
||||
hash.delete name
|
||||
|
@ -1253,18 +1269,66 @@ class Type < Puppet::Element
|
|||
self.schedule
|
||||
end
|
||||
|
||||
def schedule
|
||||
unless self[:schedule]
|
||||
return
|
||||
end
|
||||
# Return a cached value
|
||||
def cached(name)
|
||||
Puppet::Storage.cache(self)[name]
|
||||
#@cache[name] ||= nil
|
||||
end
|
||||
|
||||
if sched = Puppet.type(:schedule)[self[:schedule]]
|
||||
self[:schedule] = sched
|
||||
# Cache a value
|
||||
def cache(name, value)
|
||||
Puppet::Storage.cache(self)[name] = value
|
||||
#@cache[name] = value
|
||||
end
|
||||
|
||||
# Look up the schedule and set it appropriately. This is done after
|
||||
# the instantiation phase, so that the schedule can be anywhere in the
|
||||
# file.
|
||||
def schedule
|
||||
|
||||
# If we've already set the schedule, then just move on
|
||||
return if self[:schedule].is_a?(Puppet.type(:schedule))
|
||||
|
||||
# Schedules don't need to be scheduled
|
||||
return if self.is_a?(Puppet.type(:schedule))
|
||||
|
||||
# Nor do components
|
||||
return if self.is_a?(Puppet.type(:component))
|
||||
|
||||
if self[:schedule]
|
||||
if sched = Puppet.type(:schedule)[self[:schedule]]
|
||||
self[:schedule] = sched
|
||||
else
|
||||
self.fail "Could not find schedule %s" % self[:schedule]
|
||||
end
|
||||
elsif Puppet[:schedule] and ! Puppet[:ignoreschedules]
|
||||
# We handle schedule defaults here mostly because otherwise things
|
||||
# will behave very very erratically during testing.
|
||||
if sched = Puppet.type(:schedule)[Puppet[:schedule]]
|
||||
self[:schedule] = sched
|
||||
else
|
||||
self.fail "Could not find default schedule %s" % Puppet[:schedule]
|
||||
end
|
||||
else
|
||||
self.fail "Could not find schedule %s" % self[:schedule]
|
||||
# While it's unlikely we won't have any schedule (since there's a
|
||||
# default), it's at least possible during testing
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
# Check whether we are scheduled to run right now or not.
|
||||
def scheduled?
|
||||
return true if Puppet[:ignoreschedules]
|
||||
return true unless schedule = self[:schedule]
|
||||
|
||||
# We use 'checked' here instead of 'synced' because otherwise we'll
|
||||
# end up checking most elements most times, because they will generally
|
||||
# have been synced a long time ago (e.g., a file only gets updated
|
||||
# once a month on the server and its schedule is daily; the last sync time
|
||||
# will have been a month ago, so we'd end up checking every run).
|
||||
return schedule.match?(self.cached(:checked))
|
||||
end
|
||||
|
||||
# Is the specified parameter set?
|
||||
def attrset?(type, attr)
|
||||
case type
|
||||
|
@ -1776,8 +1840,29 @@ class Type < Puppet::Element
|
|||
end
|
||||
|
||||
newmetaparam(:schedule) do
|
||||
desc "On what schedule the object should be managed.
|
||||
Currently non-functional."
|
||||
desc "On what schedule the object should be managed. You must create a
|
||||
schedule_ object, and then reference the name of that object to use
|
||||
that for your schedule:
|
||||
|
||||
schedule { daily:
|
||||
period => daily,
|
||||
range => \"2-4\"
|
||||
}
|
||||
|
||||
exec { \"/usr/bin/apt-get update\":
|
||||
schedule => daily
|
||||
}
|
||||
|
||||
The creation of the schedule object does not need to appear in the
|
||||
configuration before objects that use it."
|
||||
|
||||
munge do |name|
|
||||
if schedule = Puppet.type(:schedule)[name]
|
||||
return schedule
|
||||
else
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newmetaparam(:check) do
|
||||
|
|
|
@ -238,7 +238,7 @@ module Puppet
|
|||
|
||||
return str
|
||||
else
|
||||
Puppet.notice "No host instances for %s" % user
|
||||
Puppet.notice "No host instances"
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,27 @@ module Puppet
|
|||
@checktypes[0]
|
||||
end
|
||||
|
||||
# Checksums need to invert how changes are printed.
|
||||
def change_to_s
|
||||
begin
|
||||
if @is == :absent
|
||||
return "defined '%s' as '%s'" %
|
||||
[self.name, self.should_to_s]
|
||||
elsif self.should == :absent
|
||||
return "undefined %s from '%s'" %
|
||||
[self.name, self.is_to_s]
|
||||
else
|
||||
return "%s changed '%s' to '%s'" %
|
||||
[self.name, self.should_to_s, self.is_to_s]
|
||||
end
|
||||
rescue Puppet::Error, Puppet::DevError
|
||||
raise
|
||||
rescue => detail
|
||||
raise Puppet::DevError, "Could not convert change %s to string: %s" %
|
||||
[self.name, detail]
|
||||
end
|
||||
end
|
||||
|
||||
def getsum(checktype)
|
||||
sum = ""
|
||||
case checktype
|
||||
|
@ -85,27 +106,45 @@ module Puppet
|
|||
end
|
||||
|
||||
@checktypes << value
|
||||
state = Puppet::Storage.state(self)
|
||||
|
||||
unless state
|
||||
self.devfail "Did not get state back from Storage"
|
||||
hash = nil
|
||||
unless hash = @parent.cached(:checksums)
|
||||
hash = {}
|
||||
@parent.cache(:checksums, hash)
|
||||
end
|
||||
if hash = state[@parent[:path]]
|
||||
if hash.include?(value)
|
||||
#self.notice "Found checksum %s for %s" %
|
||||
# [hash[value] ,@parent[:path]]
|
||||
return hash[value]
|
||||
else
|
||||
#self.notice "Found checksum for %s but not of type %s" %
|
||||
# [@parent[:path],@checktypes[0]]
|
||||
return :nosum
|
||||
end
|
||||
else
|
||||
# We can't use :absent here, because then it'll match on
|
||||
# non-existent files
|
||||
|
||||
#unless state
|
||||
# self.devfail "Did not get state back from Storage"
|
||||
#end
|
||||
|
||||
if hash.include?(value)
|
||||
#self.notice "Found checksum %s for %s" %
|
||||
# [hash[value] ,@parent[:path]]
|
||||
return hash[value]
|
||||
elsif hash.empty?
|
||||
#self.notice "Could not find sum of type %s" % @checktypes[0]
|
||||
return :nosum
|
||||
else
|
||||
#self.notice "Found checksum for %s but not of type %s" %
|
||||
# [@parent[:path],@checktypes[0]]
|
||||
return :nosum
|
||||
end
|
||||
# if hash = state[@parent[:path]]
|
||||
# if hash.include?(value)
|
||||
# #self.notice "Found checksum %s for %s" %
|
||||
# # [hash[value] ,@parent[:path]]
|
||||
# return hash[value]
|
||||
# else
|
||||
# #self.notice "Found checksum for %s but not of type %s" %
|
||||
# # [@parent[:path],@checktypes[0]]
|
||||
# return :nosum
|
||||
# end
|
||||
# else
|
||||
# # We can't use :absent here, because then it'll match on
|
||||
# # non-existent files
|
||||
# #self.notice "Could not find sum of type %s" % @checktypes[0]
|
||||
# return :nosum
|
||||
# end
|
||||
end
|
||||
|
||||
# Even though they can specify multiple checksums, the insync?
|
||||
|
@ -188,11 +227,11 @@ module Puppet
|
|||
# Store the new sum to the state db.
|
||||
def updatesum
|
||||
result = false
|
||||
state = Puppet::Storage.state(self)
|
||||
unless state.include?(@parent.name)
|
||||
self.debug "Initializing state hash for %s" % @parent.name
|
||||
|
||||
state[@parent.name] = Hash.new
|
||||
state = nil
|
||||
unless state = @parent.cached(:checksums)
|
||||
self.debug "Initializing checksum hash for %s" % @parent.name
|
||||
state = {}
|
||||
@parent.cache(:checksums, state)
|
||||
end
|
||||
|
||||
if @is.is_a?(Symbol)
|
||||
|
@ -206,7 +245,7 @@ module Puppet
|
|||
end
|
||||
|
||||
# if we're replacing, vs. updating
|
||||
if state[@parent.name].include?(@checktypes[0])
|
||||
if state.include?(@checktypes[0])
|
||||
unless defined? @should
|
||||
raise Puppet::Error.new(
|
||||
("@should is not initialized for %s, even though we " +
|
||||
|
@ -214,7 +253,7 @@ module Puppet
|
|||
)
|
||||
end
|
||||
self.debug "Replacing %s checksum %s with %s" %
|
||||
[@parent.name, state[@parent.name][@checktypes[0]],@is]
|
||||
[@parent.name, state[@checktypes[0]],@is]
|
||||
#@parent.debug "@is: %s; @should: %s" % [@is,@should]
|
||||
result = true
|
||||
else
|
||||
|
@ -222,7 +261,7 @@ module Puppet
|
|||
[@is,@checktypes[0]]
|
||||
result = false
|
||||
end
|
||||
state[@parent.name][@checktypes[0]] = @is
|
||||
state[@checktypes[0]] = @is
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
module Puppet
|
||||
newtype(:schedule) do
|
||||
@doc = "Defined schedules for Puppet. The important thing to understand
|
||||
about how schedules are currently implemented in Puppet is that they
|
||||
can only be used to stop an element from being applied, they never
|
||||
guarantee that it is applied.
|
||||
|
||||
Every time Puppet applies its configuration, it will collect the
|
||||
list of elements whose schedule does not eliminate them from
|
||||
running right then, but there is currently no system in place to
|
||||
guarantee that a given element runs at a given time. If you
|
||||
specify a very restrictive schedule and Puppet happens to run at a
|
||||
time within that schedule, then the elements will get applied;
|
||||
otherwise, that work may never get done.
|
||||
|
||||
Thus, it behooves you to use wider scheduling (e.g., over a couple of
|
||||
hours) combined with periods and repetitions. For instance, if you
|
||||
wanted to restrict certain elements to only running once, between
|
||||
the hours of two and 4 AM, then you would use this schedule::
|
||||
|
||||
schedule { maint:
|
||||
range => \"2 - 4\",
|
||||
period => daily,
|
||||
repeat => 1
|
||||
}
|
||||
|
||||
With this schedule, the first time that Puppet runs between 2 and 4 AM,
|
||||
all elements with this schedule will get applied, but they won't
|
||||
get applied again between 2 and 4 because they will have already
|
||||
run once that day, and they won't get applied outside that schedule
|
||||
because they will be outside the scheduled range.
|
||||
|
||||
Puppet automatically creates a schedule for each valid period with the
|
||||
same name as that period (e.g., hourly and daily). Additionally,
|
||||
a schedule named *puppet* is created and used as the default,
|
||||
with the following attributes:
|
||||
|
||||
schedule { puppet:
|
||||
period => hourly,
|
||||
repeat => 2
|
||||
}
|
||||
|
||||
This will cause elements to be applied every 30 minutes by default.
|
||||
"
|
||||
|
||||
@states = []
|
||||
|
||||
newparam(:name) do
|
||||
desc "The name of the schedule. This name is used to retrieve the
|
||||
schedule when assigning it to an object::
|
||||
|
||||
schedule { daily:
|
||||
period => daily,
|
||||
range => [2, 4]
|
||||
}
|
||||
|
||||
exec { \"/usr/bin/apt-get update\":
|
||||
schedule => daily
|
||||
}
|
||||
|
||||
"
|
||||
isnamevar
|
||||
end
|
||||
|
||||
newparam(:range) do
|
||||
desc "The earliest and latest that an element can be applied. This
|
||||
is always a range within a 24 hour period, and hours must be
|
||||
specified in numbers between 0 and 23, inclusive. Minutes and
|
||||
seconds can be provided, using the normal colon as a separator.
|
||||
For instance::
|
||||
|
||||
schedule { maintenance:
|
||||
range => \"1:30 - 4:30\"
|
||||
}
|
||||
|
||||
This is mostly useful for restricting certain elements to being
|
||||
applied in maintenance windows or during off-peak hours."
|
||||
|
||||
# This is lame; states all use arrays as values, but parameters don't.
|
||||
# That's going to hurt eventually.
|
||||
validate do |values|
|
||||
values = [values] unless values.is_a?(Array)
|
||||
values.each { |value|
|
||||
unless value.is_a?(String) and
|
||||
value =~ /\d+(:\d+){0,2}\s*-\s*\d+(:\d+){0,2}/
|
||||
self.fail "Invalid range value '%s'" % value
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
munge do |values|
|
||||
values = [values] unless values.is_a?(Array)
|
||||
ret = []
|
||||
|
||||
values.each { |value|
|
||||
range = []
|
||||
# Split each range value into a hour, minute, second triad
|
||||
value.split(/\s*-\s*/).each { |val|
|
||||
# Add the values as an array.
|
||||
range << val.split(":").collect { |n| n.to_i }
|
||||
}
|
||||
|
||||
if range.length != 2
|
||||
self.fail "Invalid range %s" % value
|
||||
end
|
||||
|
||||
if range[0][0] > range[1][0]
|
||||
self.fail(("Invalid range %s; " % value) +
|
||||
"ranges cannot span days."
|
||||
)
|
||||
end
|
||||
ret << range
|
||||
}
|
||||
|
||||
# Now our array of arrays
|
||||
ret
|
||||
end
|
||||
|
||||
def match?(previous, now)
|
||||
# The lowest-level array is of the hour, minute, second triad
|
||||
# then it's an array of two of those, to present the limits
|
||||
# then it's array of those ranges
|
||||
unless @value[0][0].is_a?(Array)
|
||||
@value = [@value]
|
||||
end
|
||||
|
||||
@value.each do |value|
|
||||
limits = value.collect do |range|
|
||||
ary = [now.year, now.month, now.day, range[0]]
|
||||
if range[1]
|
||||
ary << range[1]
|
||||
else
|
||||
ary << now.min
|
||||
end
|
||||
|
||||
if range[2]
|
||||
ary << range[2]
|
||||
else
|
||||
ary << now.sec
|
||||
end
|
||||
|
||||
time = Time.local(*ary)
|
||||
|
||||
unless time.hour == range[0]
|
||||
self.devfail(
|
||||
"Incorrectly converted time: %s: %s vs %s" %
|
||||
[time, time.hour, range[0]]
|
||||
)
|
||||
end
|
||||
|
||||
time
|
||||
end
|
||||
|
||||
unless limits[0] < limits[1]
|
||||
self.info(
|
||||
"Assuming upper limit should be that time the next day"
|
||||
)
|
||||
|
||||
ary = limits[1].to_a
|
||||
ary[3] += 1
|
||||
limits[1] = Time.local(*ary)
|
||||
|
||||
#self.devfail("Lower limit is above higher limit: %s" %
|
||||
# limits.inspect
|
||||
#)
|
||||
end
|
||||
|
||||
#self.info limits.inspect
|
||||
#self.notice now
|
||||
return now.between?(*limits)
|
||||
end
|
||||
|
||||
# Else, return false, since our current time isn't between
|
||||
# any valid times
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:periodmatch) do
|
||||
desc "Whether periods should be matched by number (e.g., the two times
|
||||
are in the same hour) or by distance (e.g., the two times are
|
||||
60 minutes apart). *number*/**distance**"
|
||||
|
||||
newvalues(:number, :distance)
|
||||
|
||||
defaultto :distance
|
||||
end
|
||||
|
||||
newparam(:period) do
|
||||
desc "The period of repetition for an element. Choose from among
|
||||
a fixed list of *hourly*, *daily*, *weekly*, and *monthly*.
|
||||
The default is for an element to get applied every time that
|
||||
Puppet runs, whatever that period is.
|
||||
|
||||
Note that the period defines how often a given element will get
|
||||
applied but not when; if you would like to restrict the hours
|
||||
that a given element can be applied (e.g., only at night during
|
||||
a maintenance window) then use the ``range`` attribute.
|
||||
|
||||
If the provided periods are not sufficient, you can provide a
|
||||
value to the *repeat* attribute, which will cause Puppet to
|
||||
schedule the affected elements evenly in the period the
|
||||
specified number of times. Take this schedule::
|
||||
|
||||
schedule { veryoften:
|
||||
period => hourly,
|
||||
repeat => 6
|
||||
}
|
||||
|
||||
This can cause Puppet to apply that element up to every 10 minutes.
|
||||
|
||||
At the moment, Puppet cannot guarantee that level of
|
||||
repetition; that is, it can run up to every 10 minutes, but
|
||||
internal factors might prevent it from actually running that
|
||||
often (e.g., long-running Puppet runs will squash conflictingly
|
||||
scheduled runs).
|
||||
|
||||
See the ``periodmatch`` attribute for tuning whether to match
|
||||
times by their distance apart or by their specific value."
|
||||
|
||||
newvalues(:hourly, :daily, :weekly, :monthly)
|
||||
|
||||
@@scale = {
|
||||
:hourly => 3600,
|
||||
:daily => 86400,
|
||||
:weekly => 604800,
|
||||
:monthly => 2592000
|
||||
}
|
||||
@@methods = {
|
||||
:hourly => :hour,
|
||||
:daily => :day,
|
||||
:monthly => :month,
|
||||
:weekly => proc do |prev, now|
|
||||
prev.strftime("%U") == now.strftime("%U")
|
||||
end
|
||||
}
|
||||
|
||||
def match?(previous, now)
|
||||
value = self.value
|
||||
case @parent[:periodmatch]
|
||||
when :number
|
||||
method = @@methods[value]
|
||||
if method.is_a?(Proc)
|
||||
return method.call(previous, now)
|
||||
else
|
||||
# We negate it, because if they're equal we don't run
|
||||
val = now.send(method) != previous.send(method)
|
||||
return val
|
||||
end
|
||||
when :distance
|
||||
scale = @@scale[value]
|
||||
|
||||
# If the number of seconds between the two times is greater
|
||||
# than the unit of time, we match. We divide the scale
|
||||
# by the repeat, so that we'll repeat that often within
|
||||
# the scale.
|
||||
return (now.to_i - previous.to_i) >= (scale / @parent[:repeat])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
newparam(:repeat) do
|
||||
desc "How often the application gets repeated in a given period.
|
||||
Defaults to 1."
|
||||
|
||||
defaultto 1
|
||||
|
||||
validate do |value|
|
||||
unless value.is_a?(Integer) or value =~ /^\d+$/
|
||||
raise Puppet::Error,
|
||||
"Repeat must be a number"
|
||||
end
|
||||
|
||||
# This implicitly assumes that 'periodmatch' is distance -- that
|
||||
# is, if there's no value, we assume it's a valid value.
|
||||
return unless @parent[:periodmatch]
|
||||
|
||||
if value != 1 and @parent[:periodmatch] != :distance
|
||||
raise Puppet::Error,
|
||||
"Repeat must be 1 unless periodmatch is 'distance', not '%s'" %
|
||||
@parent[:periodmatch]
|
||||
end
|
||||
end
|
||||
|
||||
munge do |value|
|
||||
unless value.is_a?(Integer)
|
||||
value = Integer(value)
|
||||
end
|
||||
|
||||
value
|
||||
end
|
||||
|
||||
def match?(previous, now)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def self.mkdefaultschedules
|
||||
Puppet.info "Creating default schedules"
|
||||
# Create our default schedule
|
||||
self.create(
|
||||
:name => "puppet",
|
||||
:period => :hourly,
|
||||
:repeat => "2"
|
||||
)
|
||||
|
||||
# And then one for every period
|
||||
@parameters.find { |p| p.name == :period }.values.each { |value|
|
||||
self.create(
|
||||
:name => value.to_s,
|
||||
:period => value
|
||||
)
|
||||
}
|
||||
end
|
||||
|
||||
def match?(previous = nil, now = nil)
|
||||
|
||||
# If we've got a value, then convert it to a Time instance
|
||||
if previous
|
||||
previous = Time.at(previous)
|
||||
end
|
||||
|
||||
now ||= Time.now
|
||||
|
||||
# Pull them in order
|
||||
self.class.allattrs.each { |param|
|
||||
if @parameters.include?(param) and
|
||||
@parameters[param].respond_to?(:match?)
|
||||
#self.notice "Trying to match %s" % param
|
||||
return false unless @parameters[param].match?(previous, now)
|
||||
end
|
||||
}
|
||||
|
||||
# If we haven't returned false, then return true; in other words,
|
||||
# any provided schedules need to all match
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# $Id$
|
|
@ -28,32 +28,32 @@ class State < Puppet::Parameter
|
|||
end
|
||||
end
|
||||
|
||||
# Define a new value for our state.
|
||||
# Parameters just use 'newvalues', since there's no work associated with them.
|
||||
def self.newvalue(name, &block)
|
||||
@statevalues ||= {}
|
||||
@parametervalues ||= {}
|
||||
|
||||
if @statevalues.include?(name)
|
||||
Puppet.warning "%s already has a value for %s" % [self.name, name]
|
||||
if @parametervalues.include?(name)
|
||||
Puppet.warning "%s already has a value for %s" % [name, name]
|
||||
end
|
||||
@statevalues[name] = block
|
||||
@parametervalues[name] = block
|
||||
|
||||
define_method("set_" + name.to_s, &block)
|
||||
end
|
||||
|
||||
def self.aliasvalue(name, other)
|
||||
@statevalues ||= {}
|
||||
unless @statevalues.include?(other)
|
||||
raise Puppet::DevError, "Cannot alias nonexistent value %s" % other
|
||||
end
|
||||
|
||||
@aliasvalues ||= {}
|
||||
@aliasvalues[name] = other
|
||||
end
|
||||
|
||||
def self.alias(name)
|
||||
@aliasvalues[name]
|
||||
end
|
||||
|
||||
#
|
||||
# def self.aliasvalue(name, other)
|
||||
# @statevalues ||= {}
|
||||
# unless @statevalues.include?(other)
|
||||
# raise Puppet::DevError, "Cannot alias nonexistent value %s" % other
|
||||
# end
|
||||
#
|
||||
# @aliasvalues ||= {}
|
||||
# @aliasvalues[name] = other
|
||||
# end
|
||||
#
|
||||
# def self.alias(name)
|
||||
# @aliasvalues[name]
|
||||
# end
|
||||
#
|
||||
def self.defaultvalues
|
||||
newvalue(:present) do
|
||||
@parent.create
|
||||
|
@ -66,15 +66,15 @@ class State < Puppet::Parameter
|
|||
# This doc will probably get overridden
|
||||
@doc ||= "The basic state that the object should be in."
|
||||
end
|
||||
|
||||
# Return the list of valid values.
|
||||
def self.values
|
||||
@statevalues ||= {}
|
||||
@aliasvalues ||= {}
|
||||
|
||||
#[@aliasvalues.keys, @statevalues.keys].flatten
|
||||
@statevalues.keys
|
||||
end
|
||||
#
|
||||
# # Return the list of valid values.
|
||||
# def self.values
|
||||
# @statevalues ||= {}
|
||||
# @aliasvalues ||= {}
|
||||
#
|
||||
# #[@aliasvalues.keys, @statevalues.keys].flatten
|
||||
# @statevalues.keys
|
||||
# end
|
||||
|
||||
# Call the method associated with a given value.
|
||||
def set
|
||||
|
@ -278,38 +278,38 @@ class State < Puppet::Parameter
|
|||
self.set
|
||||
end
|
||||
|
||||
munge do |value|
|
||||
if self.class.values.empty?
|
||||
# This state isn't using defined values to do its work.
|
||||
return value
|
||||
end
|
||||
intern = value.to_s.intern
|
||||
# If it's a valid value, always return it as a symbol.
|
||||
if self.class.values.include?(intern)
|
||||
retval = intern
|
||||
elsif other = self.class.alias(intern)
|
||||
self.info "returning alias %s for %s" % [other, intern]
|
||||
retval = other
|
||||
else
|
||||
retval = value
|
||||
end
|
||||
retval
|
||||
end
|
||||
|
||||
# Verify that the passed value is valid.
|
||||
validate do |value|
|
||||
if self.class.values.empty?
|
||||
# This state isn't using defined values to do its work.
|
||||
return
|
||||
end
|
||||
unless value.is_a?(Symbol)
|
||||
value = value.to_s.intern
|
||||
end
|
||||
unless self.class.values.include?(value) or self.class.alias(value)
|
||||
self.fail "Invalid '%s' value '%s'. Valid values are '%s'" %
|
||||
[self.class.name, value, self.class.values.join(", ")]
|
||||
end
|
||||
end
|
||||
# munge do |value|
|
||||
# if self.class.values.empty?
|
||||
# # This state isn't using defined values to do its work.
|
||||
# return value
|
||||
# end
|
||||
# intern = value.to_s.intern
|
||||
# # If it's a valid value, always return it as a symbol.
|
||||
# if self.class.values.include?(intern)
|
||||
# retval = intern
|
||||
# elsif other = self.class.alias(intern)
|
||||
# self.info "returning alias %s for %s" % [other, intern]
|
||||
# retval = other
|
||||
# else
|
||||
# retval = value
|
||||
# end
|
||||
# retval
|
||||
# end
|
||||
#
|
||||
# # Verify that the passed value is valid.
|
||||
# validate do |value|
|
||||
# if self.class.values.empty?
|
||||
# # This state isn't using defined values to do its work.
|
||||
# return
|
||||
# end
|
||||
# unless value.is_a?(Symbol)
|
||||
# value = value.to_s.intern
|
||||
# end
|
||||
# unless self.class.values.include?(value) or self.class.alias(value)
|
||||
# self.fail "Invalid '%s' value '%s'. Valid values are '%s'" %
|
||||
# [self.class.name, value, self.class.values.join(", ")]
|
||||
# end
|
||||
# end
|
||||
|
||||
# How should a state change be printed as a string?
|
||||
def change_to_s
|
||||
|
@ -378,7 +378,6 @@ class State < Puppet::Parameter
|
|||
end
|
||||
|
||||
def retrieve
|
||||
self.warning "retrieving"
|
||||
if @parent.exists?
|
||||
@is = :present
|
||||
else
|
||||
|
|
|
@ -114,11 +114,11 @@ class TestClient < Test::Unit::TestCase
|
|||
|
||||
# now verify that our client cannot do non-cert operations
|
||||
# because its certs are signed by a different CA
|
||||
assert_raise(Puppet::NetworkClientError,
|
||||
assert_raise(Puppet::Error,
|
||||
"Client was allowed to call getconfig with no certs") {
|
||||
nonemaster.getconfig
|
||||
}
|
||||
assert_raise(Puppet::NetworkClientError,
|
||||
assert_raise(Puppet::Error,
|
||||
"Client was allowed to call getconfig with untrusted certs") {
|
||||
certmaster.getconfig
|
||||
}
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
if __FILE__ == $0
|
||||
$:.unshift '..'
|
||||
$:.unshift '../../lib'
|
||||
$puppetbase = "../../../../language/trunk"
|
||||
end
|
||||
|
||||
require 'puppet'
|
||||
require 'puppettest'
|
||||
require 'puppet/storage'
|
||||
require 'test/unit'
|
||||
|
||||
# $Id$
|
||||
|
||||
class StorageTestingClass
|
||||
end
|
||||
|
||||
class TestStorage < Test::Unit::TestCase
|
||||
include TestPuppet
|
||||
def test_simple
|
||||
state = nil
|
||||
assert_nothing_raised {
|
||||
Puppet::Storage.load
|
||||
}
|
||||
assert_nothing_raised {
|
||||
state = Puppet::Storage.state(Puppet::Type)
|
||||
}
|
||||
assert(state)
|
||||
state["/etc/passwd"] = ["md5","9ebebe0c02445c40b9dc6871b64ee416"]
|
||||
assert_nothing_raised {
|
||||
Puppet::Storage.store
|
||||
}
|
||||
|
||||
# clear the memory, so we're sure we're hitting the state file
|
||||
assert_nothing_raised {
|
||||
Puppet::Storage.clear
|
||||
Puppet::Storage.init
|
||||
}
|
||||
assert_nothing_raised {
|
||||
Puppet::Storage.load
|
||||
}
|
||||
assert_equal(
|
||||
["md5","9ebebe0c02445c40b9dc6871b64ee416"],
|
||||
Puppet::Storage.state(Puppet::Type)["/etc/passwd"]
|
||||
)
|
||||
end
|
||||
|
||||
def test_instance
|
||||
file = nil
|
||||
state = nil
|
||||
assert_nothing_raised {
|
||||
file = Puppet.type(:file).create(
|
||||
:path => "/etc/passwd"
|
||||
)
|
||||
}
|
||||
assert_nothing_raised {
|
||||
Puppet::Storage.load
|
||||
}
|
||||
assert_nothing_raised {
|
||||
state = Puppet::Storage.state(file)
|
||||
}
|
||||
assert(state)
|
||||
end
|
||||
|
||||
def test_update
|
||||
state = Puppet::Storage.state(StorageTestingClass)
|
||||
state["testing"] = "yayness"
|
||||
Puppet::Storage.store
|
||||
assert(FileTest.exists?(Puppet[:checksumfile]))
|
||||
end
|
||||
|
||||
def test_hashstorage
|
||||
state = Puppet::Storage.state(StorageTestingClass)
|
||||
hash = {
|
||||
:yay => "boo",
|
||||
:rah => "foo"
|
||||
}
|
||||
state["testing"] = hash
|
||||
Puppet::Storage.store
|
||||
Puppet::Storage.clear
|
||||
Puppet::Storage.init
|
||||
Puppet::Storage.load
|
||||
state = Puppet::Storage.state(StorageTestingClass)
|
||||
assert_equal(hash, state["testing"])
|
||||
end
|
||||
end
|
|
@ -11,12 +11,28 @@ require 'test/unit'
|
|||
class TestParsedFile < Test::Unit::TestCase
|
||||
include TestPuppet
|
||||
|
||||
def mkfile
|
||||
path = tempfile()
|
||||
File.open(path, "w") { |f| f.puts :yayness }
|
||||
|
||||
f = Puppet.type(:file).create(
|
||||
:name => path,
|
||||
:check => %w{checksum type}
|
||||
)
|
||||
|
||||
return f
|
||||
end
|
||||
|
||||
def test_storeandretrieve
|
||||
path = tempfile()
|
||||
|
||||
f = mkfile()
|
||||
|
||||
hash = {:a => :b, :c => :d}
|
||||
|
||||
state = nil
|
||||
assert_nothing_raised {
|
||||
state = Puppet::Storage.state(hash)
|
||||
state = Puppet::Storage.cache(f)
|
||||
}
|
||||
|
||||
assert(!state.include?("name"))
|
||||
|
@ -34,8 +50,11 @@ class TestParsedFile < Test::Unit::TestCase
|
|||
assert_nothing_raised {
|
||||
Puppet::Storage.load
|
||||
}
|
||||
|
||||
# Reset it
|
||||
state = nil
|
||||
assert_nothing_raised {
|
||||
state = Puppet::Storage.state(hash)
|
||||
state = Puppet::Storage.cache(f)
|
||||
}
|
||||
|
||||
assert_equal(state["name"], hash)
|
||||
|
@ -45,6 +64,8 @@ class TestParsedFile < Test::Unit::TestCase
|
|||
# are reading or writing the file at once
|
||||
# so we need to test that
|
||||
def test_multiwrite
|
||||
f = mkfile()
|
||||
|
||||
value = {:a => :b}
|
||||
threads = []
|
||||
9.times { |a|
|
||||
|
@ -52,7 +73,7 @@ class TestParsedFile < Test::Unit::TestCase
|
|||
9.times { |b|
|
||||
assert_nothing_raised {
|
||||
Puppet::Storage.load
|
||||
state = Puppet::Storage.state(value)
|
||||
state = Puppet::Storage.cache(f)
|
||||
value.each { |k,v| state[k] = v }
|
||||
state[:e] = rand(100)
|
||||
Puppet::Storage.store
|
||||
|
@ -68,7 +89,9 @@ class TestParsedFile < Test::Unit::TestCase
|
|||
Puppet::Storage.store
|
||||
Puppet::Storage.clear
|
||||
Puppet::Storage.load
|
||||
state = Puppet::Storage.state('newstate')
|
||||
|
||||
f = mkfile()
|
||||
state = Puppet::Storage.cache(f)
|
||||
assert_same Hash, state.class
|
||||
assert_equal 0, state.size
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ require 'test/unit'
|
|||
class TestPuppetDefaults < Test::Unit::TestCase
|
||||
include TestPuppet
|
||||
@@dirs = %w{rrddir puppetconf puppetvar logdir statedir}
|
||||
@@files = %w{logfile checksumfile manifest masterlog}
|
||||
@@files = %w{logfile statefile manifest masterlog}
|
||||
@@normals = %w{puppetport masterport server}
|
||||
@@booleans = %w{rrdgraph noop}
|
||||
|
||||
|
|
|
@ -53,6 +53,8 @@ module TestPuppet
|
|||
Puppet[:logdest] = "/dev/null"
|
||||
Puppet[:httplog] = "/dev/null"
|
||||
end
|
||||
|
||||
Puppet[:ignoreschedules] = true
|
||||
end
|
||||
|
||||
|
||||
|
@ -209,10 +211,12 @@ module TestPuppet
|
|||
trans = comp.evaluate
|
||||
}
|
||||
|
||||
events = nil
|
||||
assert_nothing_raised("Failed to evaluate transaction") {
|
||||
trans.evaluate
|
||||
events = trans.evaluate.collect { |e| e.event }
|
||||
}
|
||||
Puppet.type(:component).delete(comp)
|
||||
events
|
||||
end
|
||||
|
||||
def run_events(type, trans, events, msg)
|
||||
|
|
|
@ -36,14 +36,14 @@ class TestFile < Test::Unit::TestCase
|
|||
begin
|
||||
initstorage
|
||||
rescue
|
||||
system("rm -rf %s" % Puppet[:checksumfile])
|
||||
system("rm -rf %s" % Puppet[:statefile])
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
clearstorage
|
||||
Puppet::Storage.clear
|
||||
system("rm -rf %s" % Puppet[:checksumfile])
|
||||
system("rm -rf %s" % Puppet[:statefile])
|
||||
super
|
||||
end
|
||||
|
||||
|
@ -306,9 +306,8 @@ class TestFile < Test::Unit::TestCase
|
|||
)
|
||||
comp.push file
|
||||
trans = nil
|
||||
assert_nothing_raised() {
|
||||
trans = comp.evaluate
|
||||
}
|
||||
|
||||
file.retrieve
|
||||
|
||||
if file.name !~ /nonexists/
|
||||
sum = file.state(:checksum)
|
||||
|
@ -316,14 +315,16 @@ class TestFile < Test::Unit::TestCase
|
|||
assert(sum.insync?)
|
||||
end
|
||||
|
||||
assert_nothing_raised() {
|
||||
events = trans.evaluate.collect { |e| e.event }
|
||||
}
|
||||
# we don't want to kick off an event the first time we
|
||||
# come across a file
|
||||
assert(
|
||||
! events.include?(:file_changed)
|
||||
)
|
||||
events = assert_apply(comp)
|
||||
|
||||
assert(! events.include?(:file_changed),
|
||||
"File incorrectly changed")
|
||||
assert_events([], comp)
|
||||
|
||||
# We have to sleep because the time resolution of the time-based
|
||||
# mechanisms is greater than one second
|
||||
sleep 1
|
||||
|
||||
assert_nothing_raised() {
|
||||
File.open(path,File::CREAT|File::TRUNC|File::WRONLY) { |of|
|
||||
of.puts "some more text, yo"
|
||||
|
@ -332,10 +333,6 @@ class TestFile < Test::Unit::TestCase
|
|||
Puppet.type(:file).clear
|
||||
Puppet.type(:component).clear
|
||||
|
||||
# We have to sleep because the time resolution of the time-based
|
||||
# mechanisms is greater than one second
|
||||
sleep 1.1
|
||||
|
||||
# now recreate the file
|
||||
assert_nothing_raised() {
|
||||
file = Puppet.type(:file).create(
|
||||
|
@ -351,11 +348,11 @@ class TestFile < Test::Unit::TestCase
|
|||
|
||||
# If the file was missing, it should not generate an event
|
||||
# when it gets created.
|
||||
if path =~ /nonexists/e
|
||||
assert_events([], comp)
|
||||
else
|
||||
#if path =~ /nonexists/
|
||||
# assert_events([], comp)
|
||||
#else
|
||||
assert_events([:file_changed], comp)
|
||||
end
|
||||
#end
|
||||
assert_nothing_raised() {
|
||||
File.unlink(path)
|
||||
File.open(path,File::CREAT|File::TRUNC|File::WRONLY) { |of|
|
||||
|
|
|
@ -52,7 +52,7 @@ class TestFileBucket < Test::Unit::TestCase
|
|||
begin
|
||||
initstorage
|
||||
rescue
|
||||
system("rm -rf %s" % Puppet[:checksumfile])
|
||||
system("rm -rf %s" % Puppet[:statefile])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class TestFileIgnoreSources < Test::Unit::TestCase
|
|||
begin
|
||||
initstorage
|
||||
rescue
|
||||
system("rm -rf %s" % Puppet[:checksumfile])
|
||||
system("rm -rf %s" % Puppet[:statefile])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class TestFileSources < Test::Unit::TestCase
|
|||
begin
|
||||
initstorage
|
||||
rescue
|
||||
system("rm -rf %s" % Puppet[:checksumfile])
|
||||
system("rm -rf %s" % Puppet[:statefile])
|
||||
end
|
||||
if defined? @port
|
||||
@port += 1
|
||||
|
|
|
@ -0,0 +1,336 @@
|
|||
if __FILE__ == $0
|
||||
$:.unshift '..'
|
||||
$:.unshift '../../lib'
|
||||
$puppetbase = "../.."
|
||||
end
|
||||
|
||||
require 'puppet'
|
||||
require 'test/unit'
|
||||
require 'puppet/type/schedule'
|
||||
require 'puppettest'
|
||||
|
||||
class TestSchedule < Test::Unit::TestCase
|
||||
include TestPuppet
|
||||
|
||||
def setup
|
||||
super
|
||||
@stype = Puppet::Type::Schedule
|
||||
|
||||
# This will probably get overridden by different tests
|
||||
@now = Time.now
|
||||
Puppet[:ignoreschedules] = false
|
||||
end
|
||||
|
||||
def mksched
|
||||
s = nil
|
||||
assert_nothing_raised {
|
||||
s = @stype.create(
|
||||
:name => "testsched"
|
||||
)
|
||||
}
|
||||
|
||||
s
|
||||
end
|
||||
|
||||
def diff(unit, incr, method, count)
|
||||
diff = @now.to_i.send(method, incr * count)
|
||||
t = Time.at(diff)
|
||||
|
||||
#Puppet.notice "%s: %s %s %s = %s" %
|
||||
# [unit, @now.send(unit), method, count, t]
|
||||
#t.strftime("%H:%M:%S")
|
||||
t
|
||||
end
|
||||
|
||||
def month(method, count)
|
||||
diff(:hour, 3600 * 24 * 30, method, count)
|
||||
end
|
||||
|
||||
def week(method, count)
|
||||
diff(:hour, 3600 * 24 * 7, method, count)
|
||||
end
|
||||
|
||||
def day(method, count)
|
||||
diff(:hour, 3600 * 24, method, count)
|
||||
end
|
||||
|
||||
def hour(method, count)
|
||||
diff(:hour, 3600, method, count)
|
||||
end
|
||||
|
||||
def min(method, count)
|
||||
diff(:min, 60, method, count)
|
||||
end
|
||||
|
||||
def sec(method, count)
|
||||
diff(:sec, 1, method, count)
|
||||
end
|
||||
|
||||
def settimes
|
||||
unless defined? @@times
|
||||
@@times = [Time.now]
|
||||
|
||||
# Make one with an edge year on each side
|
||||
ary = Time.now.to_a
|
||||
[1999, 2000, 2001].each { |y|
|
||||
ary[5] = y; @@times << Time.local(*ary)
|
||||
}
|
||||
|
||||
# And with edge hours
|
||||
ary = Time.now.to_a
|
||||
#[23, 0].each { |h| ary[2] = h; @@times << Time.local(*ary) }
|
||||
# 23 hour
|
||||
ary[2] = 23
|
||||
@@times << Time.local(*ary)
|
||||
# 0 hour, next day
|
||||
ary[2] = 0
|
||||
@@times << addday(Time.local(*ary))
|
||||
|
||||
# And with edge minutes
|
||||
#[59, 0].each { |m| ary[1] = m; @@times << Time.local(*ary) }
|
||||
ary = Time.now.to_a
|
||||
ary[1] = 59; @@times << Time.local(*ary)
|
||||
ary[1] = 0
|
||||
#if ary[2] == 23
|
||||
@@times << Time.local(*ary)
|
||||
#else
|
||||
# @@times << addday(Time.local(*ary))
|
||||
#end
|
||||
end
|
||||
|
||||
Puppet.err @@times.inspect
|
||||
|
||||
@@times.each { |time|
|
||||
@now = time
|
||||
yield time
|
||||
}
|
||||
|
||||
@now = Time.now
|
||||
end
|
||||
|
||||
def test_range
|
||||
s = mksched
|
||||
|
||||
ary = @now.to_a
|
||||
ary[2] = 12
|
||||
@now = Time.local(*ary)
|
||||
data = {
|
||||
true => [
|
||||
# An hour previous, an hour after
|
||||
[hour("-", 1), hour("+", 1)],
|
||||
|
||||
# an hour previous but a couple minutes later, and an hour plus
|
||||
[min("-", 57), hour("+", 1)]
|
||||
],
|
||||
false => [
|
||||
# five minutes from now, an hour from now
|
||||
[min("+", 5), hour("+", 1)],
|
||||
|
||||
# an hour ago, 20 minutes ago
|
||||
[hour("-", 1), min("-", 20)]
|
||||
]
|
||||
}
|
||||
|
||||
data.each { |result, values|
|
||||
values = values.collect { |value|
|
||||
"%s - %s" % [value[0].strftime("%H:%M:%S"),
|
||||
value[1].strftime("%H:%M:%S")]
|
||||
}
|
||||
values.each { |value|
|
||||
assert_nothing_raised("Could not parse %s" % value) {
|
||||
s[:range] = value
|
||||
}
|
||||
|
||||
assert_equal(result, s.match?(nil, @now),
|
||||
"%s matched %s incorrectly" % [value.inspect, @now])
|
||||
}
|
||||
|
||||
assert_nothing_raised("Could not parse %s" % values) {
|
||||
s[:range] = values
|
||||
}
|
||||
|
||||
assert_equal(result, s.match?(nil, @now),
|
||||
"%s matched %s incorrectly" % [values.inspect, @now])
|
||||
}
|
||||
end
|
||||
|
||||
def test_period_by_distance
|
||||
previous = @now
|
||||
|
||||
s = mksched
|
||||
|
||||
assert_nothing_raised {
|
||||
s[:period] = :daily
|
||||
}
|
||||
|
||||
assert(s.match?(day("-", 1)), "did not match minus a day")
|
||||
assert(s.match?(day("-", 2)), "did not match two days")
|
||||
assert(! s.match?(@now), "matched today")
|
||||
assert(! s.match?(hour("-", 11)), "matched minus 11 hours")
|
||||
|
||||
# Now test hourly
|
||||
assert_nothing_raised {
|
||||
s[:period] = :hourly
|
||||
}
|
||||
|
||||
assert(s.match?(hour("-", 1)), "did not match minus an hour")
|
||||
assert(s.match?(hour("-", 2)), "did not match two hours")
|
||||
assert(! s.match?(@now), "matched now")
|
||||
assert(! s.match?(min("-", 59)), "matched minus 11 hours")
|
||||
|
||||
# and weekly
|
||||
assert_nothing_raised {
|
||||
s[:period] = :weekly
|
||||
}
|
||||
|
||||
assert(s.match?(week("-", 1)), "did not match minus a week")
|
||||
assert(s.match?(day("-", 7)), "did not match minus 7 days")
|
||||
assert(s.match?(day("-", 8)), "did not match minus 8 days")
|
||||
assert(s.match?(week("-", 2)), "did not match two weeks")
|
||||
assert(! s.match?(@now), "matched now")
|
||||
assert(! s.match?(day("-", 6)), "matched minus 6 days")
|
||||
|
||||
# and monthly
|
||||
assert_nothing_raised {
|
||||
s[:period] = :monthly
|
||||
}
|
||||
|
||||
assert(s.match?(month("-", 1)), "did not match minus a month")
|
||||
assert(s.match?(week("-", 5)), "did not match minus 5 weeks")
|
||||
assert(s.match?(week("-", 7)), "did not match minus 7 weeks")
|
||||
assert(s.match?(day("-", 50)), "did not match minus 50 days")
|
||||
assert(s.match?(month("-", 2)), "did not match two months")
|
||||
assert(! s.match?(@now), "matched now")
|
||||
assert(! s.match?(week("-", 3)), "matched minus 3 weeks")
|
||||
assert(! s.match?(day("-", 20)), "matched minus 20 days")
|
||||
end
|
||||
|
||||
# A shortened test...
|
||||
def test_period_by_number
|
||||
s = mksched
|
||||
assert_nothing_raised {
|
||||
s[:periodmatch] = :number
|
||||
}
|
||||
|
||||
assert_nothing_raised {
|
||||
s[:period] = :daily
|
||||
}
|
||||
|
||||
assert(s.match?(day("+", 1)), "didn't match plus a day")
|
||||
assert(s.match?(week("+", 1)), "didn't match plus a week")
|
||||
assert(! s.match?(@now), "matched today")
|
||||
assert(! s.match?(hour("-", 11)), "matched minus 11 hours")
|
||||
assert(! s.match?(hour("-", 1)), "matched minus an hour")
|
||||
assert(! s.match?(hour("-", 2)), "matched plus two hours")
|
||||
|
||||
# Now test hourly
|
||||
assert_nothing_raised {
|
||||
s[:period] = :hourly
|
||||
}
|
||||
|
||||
assert(s.match?(hour("+", 1)), "did not match plus an hour")
|
||||
assert(s.match?(hour("+", 2)), "did not match plus two hours")
|
||||
assert(! s.match?(@now), "matched now")
|
||||
assert(! s.match?(sec("+", 20)), "matched plus 20 seconds")
|
||||
end
|
||||
|
||||
def test_periodmatch_default
|
||||
s = mksched
|
||||
|
||||
match = s[:periodmatch]
|
||||
assert(match, "Could not find periodmatch")
|
||||
|
||||
assert_equal(:distance, match, "Periodmatch was %s" % match)
|
||||
end
|
||||
|
||||
def test_scheduled_objects
|
||||
s = mksched
|
||||
s[:period] = :hourly
|
||||
|
||||
f = nil
|
||||
path = tempfile()
|
||||
assert_nothing_raised {
|
||||
f = Puppet.type(:file).create(
|
||||
:name => path,
|
||||
:schedule => s.name,
|
||||
:ensure => "file"
|
||||
)
|
||||
}
|
||||
|
||||
assert(f.scheduled?, "File is not scheduled to run")
|
||||
|
||||
assert_apply(f)
|
||||
|
||||
assert(! f.scheduled?, "File is scheduled to run already")
|
||||
File.unlink(path)
|
||||
|
||||
assert_apply(f)
|
||||
|
||||
assert(! FileTest.exists?(path), "File was created when not scheduled")
|
||||
end
|
||||
|
||||
def test_latebinding_schedules
|
||||
f = nil
|
||||
path = tempfile()
|
||||
assert_nothing_raised {
|
||||
f = Puppet.type(:file).create(
|
||||
:name => path,
|
||||
:schedule => "testsched",
|
||||
:ensure => "file"
|
||||
)
|
||||
}
|
||||
|
||||
s = mksched
|
||||
s[:period] = :hourly
|
||||
|
||||
assert_nothing_raised {
|
||||
f.schedule
|
||||
}
|
||||
|
||||
assert(f.scheduled?, "File is not scheduled to run")
|
||||
end
|
||||
|
||||
# Verify that each of our default schedules exist
|
||||
def test_defaultschedules
|
||||
Puppet.type(:schedule).mkdefaultschedules
|
||||
%w{puppet hourly daily weekly monthly}.each { |period|
|
||||
assert(Puppet.type(:schedule)[period], "Could not find %s schedule" %
|
||||
period)
|
||||
}
|
||||
end
|
||||
|
||||
def test_period_with_repeat
|
||||
previous = @now
|
||||
|
||||
s = mksched
|
||||
s[:period] = :hourly
|
||||
|
||||
assert_nothing_raised("Was not able to set periodmatch") {
|
||||
s[:periodmatch] = :number
|
||||
}
|
||||
assert_raise(Puppet::Error) {
|
||||
s[:repeat] = 2
|
||||
}
|
||||
assert_nothing_raised("Was not able to reset periodmatch") {
|
||||
s[:periodmatch] = :distance
|
||||
}
|
||||
|
||||
assert(! s.match?(min("-", 40)), "matched minus 40 minutes")
|
||||
|
||||
assert_nothing_raised("Was not able to set period") {
|
||||
s[:repeat] = 2
|
||||
}
|
||||
|
||||
assert(! s.match?(min("-", 20)), "matched minus 20 minutes with half-hourly")
|
||||
assert(s.match?(min("-", 40)), "Did not match minus 40 with half-hourly")
|
||||
|
||||
assert_nothing_raised("Was not able to set period") {
|
||||
s[:repeat] = 3
|
||||
}
|
||||
|
||||
assert(! s.match?(min("-", 15)), "matched minus 15 minutes with half-hourly")
|
||||
assert(s.match?(min("-", 25)), "Did not match minus 25 with half-hourly")
|
||||
end
|
||||
end
|
||||
|
||||
# $Id$
|
Загрузка…
Ссылка в новой задаче