diff options
Diffstat (limited to 'lib/puppet')
39 files changed, 680 insertions, 270 deletions
diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb index 18425c8bc..b4da770f0 100644 --- a/lib/puppet/application/master.rb +++ b/lib/puppet/application/master.rb @@ -206,6 +206,8 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License end def setup + raise Puppet::Error.new("Puppet master is not supported on Microsoft Windows") if Puppet.features.microsoft_windows? + # Handle the logging settings. if options[:debug] or options[:verbose] if options[:debug] diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb index 3d6e8557c..5581917a1 100644 --- a/lib/puppet/configurer.rb +++ b/lib/puppet/configurer.rb @@ -161,10 +161,6 @@ class Puppet::Configurer # Make sure we forget the retained module_directories of any autoload # we might have used. Thread.current[:env_module_directories] = nil - - # Now close all of our existing http connections, since there's no - # reason to leave them lying open. - Puppet::Network::HttpPool.clear_http_instances end ensure Puppet::Util::Log.close(report) diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index d714305b3..106e94b02 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -47,10 +47,14 @@ module Puppet exits. Comma-separate multiple values. For a list of all values, specify 'all'. This feature is only available in Puppet versions higher than 0.18.4."], - :color => ["ansi", "Whether to use colors when logging to the console. + :color => { + :default => (Puppet.features.microsoft_windows? ? "false" : "ansi"), + :type => :setting, + :desc => "Whether to use colors when logging to the console. Valid values are `ansi` (equivalent to `true`), `html` (mostly used during testing with TextMate), and `false`, which produces - no color."], + no color.", + }, :mkusers => [false, "Whether to create the necessary user and group that puppet agent will run as."], @@ -437,9 +441,11 @@ module Puppet authorization system for `puppet master`." ], :ca => [true, "Wether the master should function as a certificate authority."], - :modulepath => {:default => "$confdir/modules:/usr/share/puppet/modules", - :desc => "The search path for modules as a colon-separated list of - directories.", :type => :setting }, # We don't want this to be considered a file, since it's multiple files. + :modulepath => { + :default => "$confdir/modules#{File::PATH_SEPARATOR}/usr/share/puppet/modules", + :desc => "The search path for modules as a list of directories separated by the '#{File::PATH_SEPARATOR}' character.", + :type => :setting # We don't want this to be considered a file, since it's multiple files. + }, :ssl_client_header => ["HTTP_X_CLIENT_DN", "The header containing an authenticated client's SSL DN. Only used with Mongrel. This header must be set by the proxy to the authenticated client's SSL DN (e.g., `/CN=puppet.puppetlabs.com`). diff --git a/lib/puppet/feature/base.rb b/lib/puppet/feature/base.rb index fb73df019..2eddadb7a 100644 --- a/lib/puppet/feature/base.rb +++ b/lib/puppet/feature/base.rb @@ -40,7 +40,19 @@ Puppet.features.add(:posix) do end # We can use Microsoft Windows functions -Puppet.features.add(:microsoft_windows, :libs => ["sys/admin", "win32/process", "win32/dir"]) +Puppet.features.add(:microsoft_windows) do + begin + require 'sys/admin' + require 'win32/process' + require 'win32/dir' + require 'win32/service' + require 'win32ole' + require 'win32/api' + true + rescue LoadError => err + warn "Cannot run on Microsoft Windows without the sys-admin, win32-process, win32-dir & win32-service gems: #{err}" unless Puppet.features.posix? + end +end raise Puppet::Error,"Cannot determine basic system flavour" unless Puppet.features.posix? or Puppet.features.microsoft_windows? diff --git a/lib/puppet/file_bucket/dipper.rb b/lib/puppet/file_bucket/dipper.rb index d6f6a3747..870c50eec 100644 --- a/lib/puppet/file_bucket/dipper.rb +++ b/lib/puppet/file_bucket/dipper.rb @@ -35,11 +35,12 @@ class Puppet::FileBucket::Dipper begin file_bucket_file = Puppet::FileBucket::File.new(contents, :bucket_path => @local_path) files_original_path = absolutize_path(file) - dest_path = "#{@rest_path}#{file_bucket_file.name}#{files_original_path}" + dest_path = "#{@rest_path}#{file_bucket_file.name}/#{files_original_path}" + file_bucket_path = "#{@rest_path}#{file_bucket_file.checksum_type}/#{file_bucket_file.checksum_data}/#{files_original_path}" # Make a HEAD request for the file so that we don't waste time # uploading it if it already exists in the bucket. - unless Puppet::FileBucket::File.indirection.head("#{@rest_path}#{file_bucket_file.checksum_type}/#{file_bucket_file.checksum_data}#{files_original_path}") + unless Puppet::FileBucket::File.indirection.head(file_bucket_path) Puppet::FileBucket::File.indirection.save(file_bucket_file, dest_path) end diff --git a/lib/puppet/file_bucket/file.rb b/lib/puppet/file_bucket/file.rb index 08c0329f1..2a0558fde 100644 --- a/lib/puppet/file_bucket/file.rb +++ b/lib/puppet/file_bucket/file.rb @@ -15,11 +15,11 @@ class Puppet::FileBucket::File attr :bucket_path def initialize( contents, options = {} ) - raise ArgumentError if !contents.is_a?(String) - @contents = contents + raise ArgumentError.new("contents must be a String, got a #{contents.class}") unless contents.is_a?(String) + @contents = contents @bucket_path = options.delete(:bucket_path) - raise ArgumentError if options != {} + raise ArgumentError.new("Unknown option(s): #{options.keys.join(', ')}") unless options.empty? end def checksum_type diff --git a/lib/puppet/file_serving/base.rb b/lib/puppet/file_serving/base.rb index 1927b95d6..e936b5e75 100644 --- a/lib/puppet/file_serving/base.rb +++ b/lib/puppet/file_serving/base.rb @@ -49,7 +49,10 @@ class Puppet::FileServing::Base # Set our base path. attr_reader :path def path=(path) - raise ArgumentError.new("Paths must be fully qualified") unless path =~ /^#{::File::SEPARATOR}/ + unless path =~ /^#{::File::SEPARATOR}/ or path =~ /^[a-z]:[\/\\]/i + raise ArgumentError.new("Paths must be fully qualified") + end + @path = path end diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb index 387f16667..02bca1bea 100644 --- a/lib/puppet/file_serving/configuration.rb +++ b/lib/puppet/file_serving/configuration.rb @@ -1,26 +1,24 @@ +require 'monitor' require 'puppet' require 'puppet/file_serving' require 'puppet/file_serving/mount' require 'puppet/file_serving/mount/file' require 'puppet/file_serving/mount/modules' require 'puppet/file_serving/mount/plugins' -require 'puppet/util/cacher' class Puppet::FileServing::Configuration require 'puppet/file_serving/configuration/parser' - class << self - include Puppet::Util::Cacher - cached_attr(:configuration) { new } + extend MonitorMixin + + def self.configuration + synchronize do + @configuration ||= new + end end Mount = Puppet::FileServing::Mount - # Create our singleton configuration. - def self.create - configuration - end - private_class_method :new attr_reader :mounts diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb index 30fad8d1f..8bc5e256d 100644 --- a/lib/puppet/file_serving/fileset.rb +++ b/lib/puppet/file_serving/fileset.rb @@ -55,8 +55,13 @@ class Puppet::FileServing::Fileset end def initialize(path, options = {}) - path = path.chomp(File::SEPARATOR) unless path == File::SEPARATOR - raise ArgumentError.new("Fileset paths must be fully qualified") unless File.expand_path(path) == path + if Puppet.features.microsoft_windows? + # REMIND: UNC path + path = path.chomp(File::SEPARATOR) unless path =~ /^[A-Za-z]:\/$/ + else + path = path.chomp(File::SEPARATOR) unless path == File::SEPARATOR + end + raise ArgumentError.new("Fileset paths must be fully qualified: #{path}") unless File.expand_path(path) == path @path = path diff --git a/lib/puppet/file_serving/indirection_hooks.rb b/lib/puppet/file_serving/indirection_hooks.rb index 499767c41..bdcc8865e 100644 --- a/lib/puppet/file_serving/indirection_hooks.rb +++ b/lib/puppet/file_serving/indirection_hooks.rb @@ -13,6 +13,7 @@ module Puppet::FileServing::IndirectionHooks # Short-circuit to :file if it's a fully-qualified path or specifies a 'file' protocol. return PROTOCOL_MAP["file"] if request.key =~ /^#{::File::SEPARATOR}/ + return PROTOCOL_MAP["file"] if request.key =~ /^[a-z]:[\/\\]/i return PROTOCOL_MAP["file"] if request.protocol == "file" # We're heading over the wire the protocol is 'puppet' and we've got a server name or we're not named 'apply' or 'puppet' diff --git a/lib/puppet/file_serving/mount.rb b/lib/puppet/file_serving/mount.rb index 130d6aeb7..da7102fd8 100644 --- a/lib/puppet/file_serving/mount.rb +++ b/lib/puppet/file_serving/mount.rb @@ -1,6 +1,5 @@ require 'puppet/network/authstore' require 'puppet/util/logging' -require 'puppet/util/cacher' require 'puppet/file_serving' require 'puppet/file_serving/metadata' require 'puppet/file_serving/content' diff --git a/lib/puppet/file_serving/mount/file.rb b/lib/puppet/file_serving/mount/file.rb index 7d622e4bf..7f5af7f52 100644 --- a/lib/puppet/file_serving/mount/file.rb +++ b/lib/puppet/file_serving/mount/file.rb @@ -1,18 +1,15 @@ -require 'puppet/util/cacher' - require 'puppet/file_serving/mount' class Puppet::FileServing::Mount::File < Puppet::FileServing::Mount - class << self - include Puppet::Util::Cacher - - cached_attr(:localmap) do - { "h" => Facter.value("hostname"), - "H" => [Facter.value("hostname"), - Facter.value("domain")].join("."), - "d" => Facter.value("domain") - } - end + def self.localmap + @localmap ||= { + "h" => Facter.value("hostname"), + "H" => [ + Facter.value("hostname"), + Facter.value("domain") + ].join("."), + "d" => Facter.value("domain") + } end def complete_path(relative_path, node) diff --git a/lib/puppet/indirector/facts/facter.rb b/lib/puppet/indirector/facts/facter.rb index ab7378a34..6312a95fb 100644 --- a/lib/puppet/indirector/facts/facter.rb +++ b/lib/puppet/indirector/facts/facter.rb @@ -9,12 +9,12 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code def self.load_fact_plugins # Add any per-module fact directories to the factpath - module_fact_dirs = Puppet[:modulepath].split(":").collect do |d| + module_fact_dirs = Puppet[:modulepath].split(File::PATH_SEPARATOR).collect do |d| ["lib", "plugins"].map do |subdirectory| Dir.glob("#{d}/*/#{subdirectory}/facter") end end.flatten - dirs = module_fact_dirs + Puppet[:factpath].split(":") + dirs = module_fact_dirs + Puppet[:factpath].split(File::PATH_SEPARATOR) x = dirs.each do |dir| load_facts_in_dir(dir) end diff --git a/lib/puppet/indirector/file_server.rb b/lib/puppet/indirector/file_server.rb index a1a5c0a44..9516a404c 100644 --- a/lib/puppet/indirector/file_server.rb +++ b/lib/puppet/indirector/file_server.rb @@ -60,6 +60,6 @@ class Puppet::Indirector::FileServer < Puppet::Indirector::Terminus # Our fileserver configuration, if needed. def configuration - Puppet::FileServing::Configuration.create + Puppet::FileServing::Configuration.configuration end end diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb index d958a82ac..20b260b83 100644 --- a/lib/puppet/indirector/indirection.rb +++ b/lib/puppet/indirector/indirection.rb @@ -1,13 +1,11 @@ require 'puppet/util/docs' require 'puppet/indirector/envelope' require 'puppet/indirector/request' -require 'puppet/util/cacher' # The class that connects functional classes with their different collection # back-ends. Each indirection has a set of associated terminus classes, # each of which is a subclass of Puppet::Indirector::Terminus. class Puppet::Indirector::Indirection - include Puppet::Util::Cacher include Puppet::Util::Docs @@indirections = [] @@ -33,6 +31,8 @@ class Puppet::Indirector::Indirection attr_accessor :name, :model + attr_reader :termini + # Create and return our cache terminus. def cache raise(Puppet::DevError, "Tried to cache when no cache class was set") unless cache_class @@ -88,6 +88,7 @@ class Puppet::Indirector::Indirection def initialize(model, name, options = {}) @model = model @name = name + @termini = {} @cache_class = nil @terminus_class = nil @@ -313,7 +314,4 @@ class Puppet::Indirector::Indirection end klass.new end - - # Cache our terminus instances indefinitely, but make it easy to clean them up. - cached_attr(:termini) { Hash.new } end diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb index fd8d654dd..0388bd31a 100644 --- a/lib/puppet/indirector/request.rb +++ b/lib/puppet/indirector/request.rb @@ -76,7 +76,9 @@ class Puppet::Indirector::Request # because it rewrites the key. We could otherwise strip server/port/etc # info out in the REST class, but it seemed bad design for the REST # class to rewrite the key. - if key.to_s =~ /^\w+:\/\// # it's a URI + if key.to_s =~ /^[a-z]:[\/\\]/i # It's an absolute path for Windows. + @key = key + elsif key.to_s =~ /^\w+:\/\// # it's a URI set_uri_key(key) else @key = key diff --git a/lib/puppet/network/client.rb b/lib/puppet/network/client.rb index c56b21393..f9c4c5fea 100644 --- a/lib/puppet/network/client.rb +++ b/lib/puppet/network/client.rb @@ -82,11 +82,6 @@ class Puppet::Network::Client self.read_cert - # We have to start the HTTP connection manually before we start - # sending it requests or keep-alive won't work. Note that with #1010, - # we don't currently actually want keep-alive. - @driver.start if @driver.respond_to? :start and Puppet::Network::HttpPool.keep_alive? - @local = false elsif hash.include?(driverparam) @driver = hash[driverparam] diff --git a/lib/puppet/network/http_pool.rb b/lib/puppet/network/http_pool.rb index 7d227b4d4..8baf48c77 100644 --- a/lib/puppet/network/http_pool.rb +++ b/lib/puppet/network/http_pool.rb @@ -1,53 +1,14 @@ require 'puppet/ssl/host' require 'net/https' -require 'puppet/util/cacher' module Puppet::Network; end -# Manage Net::HTTP instances for keep-alive. module Puppet::Network::HttpPool - class << self - include Puppet::Util::Cacher - - private - - cached_attr(:http_cache) { Hash.new } - end - # Use the global localhost instance. def self.ssl_host Puppet::SSL::Host.localhost end - # 2008/03/23 - # LAK:WARNING: Enabling this has a high propability of - # causing corrupt files and who knows what else. See #1010. - HTTP_KEEP_ALIVE = false - - def self.keep_alive? - HTTP_KEEP_ALIVE - end - - # Clear our http cache, closing all connections. - def self.clear_http_instances - http_cache.each do |name, connection| - connection.finish if connection.started? - end - Puppet::Util::Cacher.expire - end - - # Make sure we set the driver up when we read the cert in. - def self.read_cert - if val = super # This calls read_cert from the Puppet::SSLCertificates::Support module. - # Clear out all of our connections, since they previously had no cert and now they - # should have them. - clear_http_instances - return val - else - return false - end - end - # Use cert information from a Puppet client to set up the http object. def self.cert_setup(http) # Just no-op if we don't have certs. @@ -63,21 +24,6 @@ module Puppet::Network::HttpPool # Retrieve a cached http instance if caching is enabled, else return # a new one. def self.http_instance(host, port, reset = false) - # We overwrite the uninitialized @http here with a cached one. - key = "#{host}:#{port}" - - # Return our cached instance if we've got a cache, as long as we're not - # resetting the instance. - if keep_alive? - return http_cache[key] if ! reset and http_cache[key] - - # Clean up old connections if we have them. - if http = http_cache[key] - http_cache.delete(key) - http.finish if http.started? - end - end - args = [host, port] if Puppet[:http_proxy_host] == "none" args << nil << nil @@ -97,8 +43,6 @@ module Puppet::Network::HttpPool cert_setup(http) - http_cache[key] = http if keep_alive? - http end end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index dc631979e..4fc314a6a 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -95,7 +95,7 @@ class Puppet::Node::Environment # Cache the modulepath, so that we aren't searching through # all known directories all the time. - cached_attr(:modulepath, :ttl => Puppet[:filetimeout]) do + cached_attr(:modulepath, Puppet[:filetimeout]) do dirs = self[:modulepath].split(File::PATH_SEPARATOR) dirs = ENV["PUPPETLIB"].split(File::PATH_SEPARATOR) + dirs if ENV["PUPPETLIB"] validate_dirs(dirs) @@ -103,7 +103,7 @@ class Puppet::Node::Environment # Return all modules from this environment. # Cache the list, because it can be expensive to create. - cached_attr(:modules, :ttl => Puppet[:filetimeout]) do + cached_attr(:modules, Puppet[:filetimeout]) do module_names = modulepath.collect { |path| Dir.entries(path) }.flatten.uniq module_names.collect do |path| begin @@ -114,12 +114,6 @@ class Puppet::Node::Environment end.compact end - # Cache the manifestdir, so that we aren't searching through - # all known directories all the time. - cached_attr(:manifestdir, :ttl => Puppet[:filetimeout]) do - validate_dirs(self[:manifestdir].split(File::PATH_SEPARATOR)) - end - def to_s name.to_s end @@ -136,14 +130,18 @@ class Puppet::Node::Environment end def validate_dirs(dirs) + dir_regex = Puppet.features.microsoft_windows? ? /^[A-Za-z]:#{File::SEPARATOR}/ : /^#{File::SEPARATOR}/ + # REMIND: Dir.getwd on windows returns a path containing backslashes, which when joined with + # dir containing forward slashes, breaks our regex matching. In general, path validation needs + # to be refactored which will be handled in a future commit. dirs.collect do |dir| - if dir !~ /^#{File::SEPARATOR}/ - File.join(Dir.getwd, dir) + if dir !~ dir_regex + File.expand_path(File.join(Dir.getwd, dir)) else dir end end.find_all do |p| - p =~ /^#{File::SEPARATOR}/ && FileTest.directory?(p) + p =~ dir_regex && FileTest.directory?(p) end end diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index 29d60fc66..c97f93b23 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -2,7 +2,6 @@ require 'puppet/util/methodhelper' require 'puppet/util/log_paths' require 'puppet/util/logging' require 'puppet/util/docs' -require 'puppet/util/cacher' class Puppet::Parameter include Puppet::Util @@ -10,7 +9,6 @@ class Puppet::Parameter include Puppet::Util::LogPaths include Puppet::Util::Logging include Puppet::Util::MethodHelper - include Puppet::Util::Cacher require 'puppet/parameter/value_collection' @@ -150,10 +148,6 @@ class Puppet::Parameter self.fail(Puppet::DevError, msg) end - def expirer - resource.catalog - end - def fail(*args) type = nil if args[0].is_a?(Class) diff --git a/lib/puppet/parser/type_loader.rb b/lib/puppet/parser/type_loader.rb index 1fba73d0b..68def068d 100644 --- a/lib/puppet/parser/type_loader.rb +++ b/lib/puppet/parser/type_loader.rb @@ -80,7 +80,8 @@ class Puppet::Parser::TypeLoader loaded_asts = [] files.each do |file| - unless file =~ /^#{File::SEPARATOR}/ + regex = Puppet.features.microsoft_windows? ? /^[A-Za-z]:#{File::SEPARATOR}/ : /^#{File::SEPARATOR}/ + unless file =~ regex file = File.join(dir, file) end @loading_helper.do_once(file) do diff --git a/lib/puppet/provider/group/windows_adsi.rb b/lib/puppet/provider/group/windows_adsi.rb new file mode 100644 index 000000000..4468d0071 --- /dev/null +++ b/lib/puppet/provider/group/windows_adsi.rb @@ -0,0 +1,48 @@ +require 'puppet/util/adsi' + +Puppet::Type.type(:group).provide :windows_adsi do + desc "Group management for Windows" + + defaultfor :operatingsystem => :windows + confine :operatingsystem => :windows + confine :feature => :microsoft_windows + + has_features :manages_members + + def group + @group ||= Puppet::Util::ADSI::Group.new(@resource[:name]) + end + + def members + group.members + end + + def members=(members) + group.set_members(members) + end + + def create + @group = Puppet::Util::ADSI::Group.create(@resource[:name]) + self.members = @resource[:members] + end + + def exists? + Puppet::Util::ADSI::Group.exists?(@resource[:name]) + end + + def delete + Puppet::Util::ADSI::Group.delete(@resource[:name]) + end + + def gid + nil + end + + def gid=(value) + warning "No support for managing property gid of group #{@resource[:name]} on Windows" + end + + def self.instances + Puppet::Util::ADSI::Group.map { |g| new(:ensure => :present, :name => g.name) } + end +end diff --git a/lib/puppet/provider/host/parsed.rb b/lib/puppet/provider/host/parsed.rb index 2ba01a41c..1a2bdb460 100644 --- a/lib/puppet/provider/host/parsed.rb +++ b/lib/puppet/provider/host/parsed.rb @@ -3,6 +3,9 @@ require 'puppet/provider/parsedfile' hosts = nil case Facter.value(:operatingsystem) when "Solaris"; hosts = "/etc/inet/hosts" +when "windows" + require 'win32/resolv' + hosts = Win32::Resolv.get_hosts_path else hosts = "/etc/hosts" end diff --git a/lib/puppet/provider/service/windows.rb b/lib/puppet/provider/service/windows.rb new file mode 100644 index 000000000..289be697a --- /dev/null +++ b/lib/puppet/provider/service/windows.rb @@ -0,0 +1,110 @@ +# Windows Service Control Manager (SCM) provider + +require 'win32/service' if Puppet.features.microsoft_windows? + +Puppet::Type.type(:service).provide :windows do + + desc "Support for Windows Service Control Manager (SCM). + + Services are controlled according to win32-service gem. + + * All SCM operations (start/stop/enable/disable/query) are supported. + + * Control of service groups (dependencies) is not yet supported." + + defaultfor :operatingsystem => :windows + confine :operatingsystem => :windows + + has_feature :refreshable + + def enable + w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_AUTO_START ) + raise Puppet::Error.new("Win32 service enable of #{@resource[:name]} failed" ) if( w32ss.nil? ) + rescue Win32::Service::Error => detail + raise Puppet::Error.new("Cannot enable #{@resource[:name]}, error was: #{detail}" ) + end + + def disable + w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DISABLED ) + raise Puppet::Error.new("Win32 service disable of #{@resource[:name]} failed" ) if( w32ss.nil? ) + rescue Win32::Service::Error => detail + raise Puppet::Error.new("Cannot disable #{@resource[:name]}, error was: #{detail}" ) + end + + def manual_start + w32ss = Win32::Service.configure( 'service_name' => @resource[:name], 'start_type' => Win32::Service::SERVICE_DEMAND_START ) + raise Puppet::Error.new("Win32 service manual enable of #{@resource[:name]} failed" ) if( w32ss.nil? ) + rescue Win32::Service::Error => detail + raise Puppet::Error.new("Cannot enable #{@resource[:name]} for manual start, error was: #{detail}" ) + end + + def enabled? + w32ss = Win32::Service.config_info( @resource[:name] ) + raise Puppet::Error.new("Win32 service query of #{@resource[:name]} failed" ) unless( !w32ss.nil? && w32ss.instance_of?( Struct::ServiceConfigInfo ) ) + debug("Service #{@resource[:name]} start type is #{w32ss.start_type}") + case w32ss.start_type + when Win32::Service.get_start_type(Win32::Service::SERVICE_AUTO_START), + Win32::Service.get_start_type(Win32::Service::SERVICE_BOOT_START), + Win32::Service.get_start_type(Win32::Service::SERVICE_SYSTEM_START) + :true + when Win32::Service.get_start_type(Win32::Service::SERVICE_DEMAND_START) + :manual + when Win32::Service.get_start_type(Win32::Service::SERVICE_DISABLED) + :false + else + raise Puppet::Error.new("Unknown start type: #{w32ss.start_type}") + end + rescue Win32::Service::Error => detail + raise Puppet::Error.new("Cannot get start type for #{@resource[:name]}, error was: #{detail}" ) + end + + def start + if enabled? == :false + # If disabled and not managing enable, respect disabled and fail. + if @resource[:enable].nil? + raise Puppet::Error, "Will not start disabled service #{@resource[:name]} without managing enable. Specify 'enable => false' to override." + # Otherwise start. If enable => false, we will later sync enable and + # disable the service again. + elsif @resource[:enable] == :true + enable + else + manual_start + end + end + + Win32::Service.start( @resource[:name] ) + rescue Win32::Service::Error => detail + raise Puppet::Error.new("Cannot start #{@resource[:name]}, error was: #{detail}" ) + end + + def stop + Win32::Service.stop( @resource[:name] ) + rescue Win32::Service::Error => detail + raise Puppet::Error.new("Cannot stop #{@resource[:name]}, error was: #{detail}" ) + end + + def restart + self.stop + self.start + end + + def status + w32ss = Win32::Service.status( @resource[:name] ) + raise Puppet::Error.new("Win32 service query of #{@resource[:name]} failed" ) unless( !w32ss.nil? && w32ss.instance_of?( Struct::ServiceStatus ) ) + state = case w32ss.current_state + when "stopped", "pause pending", "stop pending", "paused" then :stopped + when "running", "continue pending", "start pending" then :running + else + raise Puppet::Error.new("Unknown service state '#{w32ss.current_state}' for service '#{@resource[:name]}'") + end + debug("Service #{@resource[:name]} is #{w32ss.current_state}") + return state + rescue Win32::Service::Error => detail + raise Puppet::Error.new("Cannot get status of #{@resource[:name]}, error was: #{detail}" ) + end + + # returns all providers for all existing services and startup state + def self.instances + Win32::Service.services.collect { |s| new(:name => s.service_name) } + end +end diff --git a/lib/puppet/provider/user/windows_adsi.rb b/lib/puppet/provider/user/windows_adsi.rb new file mode 100644 index 000000000..9250def59 --- /dev/null +++ b/lib/puppet/provider/user/windows_adsi.rb @@ -0,0 +1,71 @@ +require 'puppet/util/adsi' + +Puppet::Type.type(:user).provide :windows_adsi do + desc "User management for Windows" + + defaultfor :operatingsystem => :windows + confine :operatingsystem => :windows + confine :feature => :microsoft_windows + + has_features :manages_homedir + + def user + @user ||= Puppet::Util::ADSI::User.new(@resource[:name]) + end + + def groups + user.groups.join(',') + end + + def groups=(groups) + user.set_groups(groups, @resource[:membership] == :minimum) + end + + def create + @user = Puppet::Util::ADSI::User.create(@resource[:name]) + [:comment, :home, :groups].each do |prop| + send("#{prop}=", @resource[prop]) if @resource[prop] + end + end + + def exists? + Puppet::Util::ADSI::User.exists?(@resource[:name]) + end + + def delete + Puppet::Util::ADSI::User.delete(@resource[:name]) + end + + # Only flush if we created or modified a user, not deleted + def flush + @user.commit if @user + end + + def comment + user['Description'] + end + + def comment=(value) + user['Description'] = value + end + + def home + user['HomeDirectory'] + end + + def home=(value) + user['HomeDirectory'] = value + end + + [:uid, :gid, :shell].each do |prop| + define_method(prop) { nil } + + define_method("#{prop}=") do |v| + warning "No support for managing property #{prop} of user #{@resource[:name]} on Windows" + end + end + + def self.instances + Puppet::Util::ADSI::User.map { |u| new(:ensure => :present, :name => u.name) } + end +end diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb index be6302595..ca9f25a5a 100644 --- a/lib/puppet/resource/catalog.rb +++ b/lib/puppet/resource/catalog.rb @@ -3,7 +3,6 @@ require 'puppet/indirector' require 'puppet/simple_graph' require 'puppet/transaction' -require 'puppet/util/cacher' require 'puppet/util/pson' require 'puppet/util/tagging' @@ -20,7 +19,6 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph include Puppet::Util::Tagging extend Puppet::Util::Pson - include Puppet::Util::Cacher::Expirer # The host name this is a catalog for. attr_accessor :name @@ -126,10 +124,6 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph def apply(options = {}) @applying = true - # Expire all of the resource data -- this ensures that all - # data we're operating against is entirely current. - expire - Puppet::Util::Storage.load if host_config? transaction = Puppet::Transaction.new(self, options[:report]) @@ -165,7 +159,6 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph return transaction ensure @applying = false - cleanup end # Are we in the middle of applying the catalog? @@ -200,14 +193,6 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph resource end - def dependent_data_expired?(ts) - if applying? - return super - else - return true - end - end - # Turn our catalog graph into an old-style tree of TransObjects and TransBuckets. # LAK:NOTE(20081211): This is a pre-0.25 backward compatibility method. # It can be removed as soon as xmlrpc is killed. @@ -567,11 +552,6 @@ class Puppet::Resource::Catalog < Puppet::SimpleGraph private - def cleanup - # Expire any cached data the resources are keeping. - expire - end - # Verify that the given resource isn't defined elsewhere. def fail_on_duplicate_type_and_title(resource) # Short-curcuit the common case, diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb index d65067c70..a4cbaf78a 100644 --- a/lib/puppet/ssl/certificate_authority.rb +++ b/lib/puppet/ssl/certificate_authority.rb @@ -1,6 +1,6 @@ +require 'monitor' require 'puppet/ssl/host' require 'puppet/ssl/certificate_request' -require 'puppet/util/cacher' # The class that knows how to sign certificates. It creates # a 'special' SSL::Host whose name is 'ca', thus indicating @@ -17,6 +17,8 @@ class Puppet::SSL::CertificateAuthority require 'puppet/ssl/certificate_authority/interface' require 'puppet/network/authstore' + extend MonitorMixin + class CertificateVerificationError < RuntimeError attr_accessor :error_code @@ -25,10 +27,10 @@ class Puppet::SSL::CertificateAuthority end end - class << self - include Puppet::Util::Cacher - - cached_attr(:singleton_instance) { new } + def self.singleton_instance + synchronize do + @singleton_instance ||= new + end end def self.ca? diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb index b9215effd..08a8ace1f 100644 --- a/lib/puppet/ssl/host.rb +++ b/lib/puppet/ssl/host.rb @@ -4,7 +4,6 @@ require 'puppet/ssl/key' require 'puppet/ssl/certificate' require 'puppet/ssl/certificate_request' require 'puppet/ssl/certificate_revocation_list' -require 'puppet/util/cacher' # The class that manages all aspects of our SSL certificates -- # private keys, public keys, requests, etc. @@ -27,14 +26,10 @@ class Puppet::SSL::Host # This accessor is used in instances for indirector requests to hold desired state attr_accessor :desired_state - class << self - include Puppet::Util::Cacher - - cached_attr(:localhost) do - result = new - result.generate unless result.certificate - result.key # Make sure it's read in - result + def self.localhost + @localhost ||= new.tap do |l| + l.generate unless l.certificate + l.key # Make sure it's read in end end diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb index 4472387d1..803b5c6a0 100644 --- a/lib/puppet/type.rb +++ b/lib/puppet/type.rb @@ -9,7 +9,6 @@ require 'puppet/metatype/manager' require 'puppet/util/errors' require 'puppet/util/log_paths' require 'puppet/util/logging' -require 'puppet/util/cacher' require 'puppet/file_collection/lookup' require 'puppet/util/tagging' @@ -21,7 +20,6 @@ class Type include Puppet::Util::Errors include Puppet::Util::LogPaths include Puppet::Util::Logging - include Puppet::Util::Cacher include Puppet::FileCollection::Lookup include Puppet::Util::Tagging @@ -469,12 +467,6 @@ class Type Puppet::Transaction::Event.new({:resource => self, :file => file, :line => line, :tags => tags}.merge(options)) end - # Let the catalog determine whether a given cached value is - # still valid or has expired. - def expirer - catalog - end - # retrieve the 'should' value for a specified property def should(name) name = attr_alias(name) diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index 9cac37f27..d3c66bc02 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -23,7 +23,7 @@ Puppet::Type.newtype(:file) do location, rather than using native resources, please contact Puppet Labs and we can hopefully work with you to develop a native resource to support what you are doing. - + **Autorequires:** If Puppet is managing the user or group that owns a file, the file resource will autorequire them. If Puppet is managing any parent directories of a file, the file resource will autorequire them." def self.title_patterns @@ -36,7 +36,7 @@ Puppet::Type.newtype(:file) do validate do |value| # accept various path syntaxes: lone slash, posix, win32, unc - unless (Puppet.features.posix? and value =~ /^\//) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) + unless (Puppet.features.posix? and value =~ /^\//) or (value =~ /^[A-Za-z]:\// or value =~ /^\/\/[^\/]+\/[^\/]+/) fail Puppet::Error, "File paths must be fully qualified, not '#{value}'" end end @@ -44,7 +44,21 @@ Puppet::Type.newtype(:file) do # convert the current path in an index into the collection and the last # path name. The aim is to use less storage for all common paths in a hierarchy munge do |value| - path, name = ::File.split(value.gsub(/\/+/,'/')) + # We need to save off, and remove the volume designator in the + # path if it is there, since File.split does not handle paths + # with volume designators properly, except when run on Windows. + # Since we are potentially compiling a catalog for a Windows + # machine on a non-Windows master, we need to handle this + # ourselves. + optional_volume_designator = value.match(/^([a-z]:)[\/\\].*/i) + value_without_designator = value.sub(/^(?:[a-z]:)?(.*)/i, '\1') + + path, name = ::File.split(value_without_designator.gsub(/\/+/,'/')) + + if optional_volume_designator + path = optional_volume_designator[1] + path + end + { :index => Puppet::FileCollection.collection.index(path), :name => name } end @@ -396,7 +410,7 @@ Puppet::Type.newtype(:file) do @parameters.each do |name, param| param.flush if param.respond_to?(:flush) end - @stat = nil + @stat = :needs_stat end def initialize(hash) @@ -415,7 +429,7 @@ Puppet::Type.newtype(:file) do end end - @stat = nil + @stat = :needs_stat end # Configure discovered resources to be purged. @@ -625,7 +639,7 @@ Puppet::Type.newtype(:file) do else self.fail "Could not back up files of type #{s.ftype}" end - expire + @stat = :needs_stat end def retrieve @@ -676,22 +690,27 @@ Puppet::Type.newtype(:file) do # use either 'stat' or 'lstat', and we expect the properties to use the # resulting stat object accordingly (mostly by testing the 'ftype' # value). - cached_attr(:stat) do + # + # We use the initial value :needs_stat to ensure we only stat the file once, + # but can also keep track of a failed stat (@stat == nil). This also allows + # us to re-stat on demand by setting @stat = :needs_stat. + def stat + return @stat unless @stat == :needs_stat + method = :stat # Files are the only types that support links if (self.class.name == :file and self[:links] != :follow) or self.class.name == :tidy method = :lstat end - path = self[:path] - begin + @stat = begin ::File.send(method, self[:path]) rescue Errno::ENOENT => error - return nil + nil rescue Errno::EACCES => error warning "Could not stat; permission denied" - return nil + nil end end @@ -778,7 +797,7 @@ Puppet::Type.newtype(:file) do next unless [:mode, :owner, :group, :seluser, :selrole, :seltype, :selrange].include?(thing.name) # Make sure we get a new stat objct - expire + @stat = :needs_stat currentvalue = thing.retrieve thing.sync unless thing.safe_insync?(currentvalue) end diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb index 67401505d..8653a8f7a 100755 --- a/lib/puppet/type/file/source.rb +++ b/lib/puppet/type/file/source.rb @@ -72,7 +72,7 @@ module Puppet self.fail "Could not understand source #{source}: #{detail}" end - self.fail "Cannot use URLs of type '#{uri.scheme}' as source for fileserving" unless uri.scheme.nil? or %w{file puppet}.include?(uri.scheme) + self.fail "Cannot use URLs of type '#{uri.scheme}' as source for fileserving" unless uri.scheme.nil? or %w{file puppet}.include?(uri.scheme) or (Puppet.features.microsoft_windows? and uri.scheme =~ /^[a-z]$/i) end end @@ -95,13 +95,14 @@ module Puppet end # Look up (if necessary) and return remote content. - cached_attr(:content) do + def content + return @content if @content raise Puppet::DevError, "No source for content was stored with the metadata" unless metadata.source unless tmp = Puppet::FileServing::Content.indirection.find(metadata.source) fail "Could not find any content at %s" % metadata.source end - tmp.content + @content = tmp.content end # Copy the values from the source to the resource. Yay. @@ -137,25 +138,27 @@ module Puppet ! (metadata.nil? or metadata.ftype.nil?) end + attr_writer :metadata + # Provide, and retrieve if necessary, the metadata for this file. Fail # if we can't find data about this host, and fail if there are any # problems in our query. - cached_attr(:metadata) do + def metadata + return @metadata if @metadata return nil unless value - result = nil value.each do |source| begin if data = Puppet::FileServing::Metadata.indirection.find(source) - result = data - result.source = source + @metadata = data + @metadata.source = source break end rescue => detail fail detail, "Could not retrieve file metadata for #{source}: #{detail}" end end - fail "Could not retrieve information from environment #{Puppet[:environment]} source(s) #{value.join(", ")}" unless result - result + fail "Could not retrieve information from environment #{Puppet[:environment]} source(s) #{value.join(", ")}" unless @metadata + @metadata end def local? @@ -177,6 +180,8 @@ module Puppet private def uri + return nil if metadata.source =~ /^[a-z]:[\/\\]/i # Abspath for Windows + @uri ||= URI.parse(URI.escape(metadata.source)) end end diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb index 3116f5f8e..eaf2b8ee1 100644 --- a/lib/puppet/type/service.rb +++ b/lib/puppet/type/service.rb @@ -47,9 +47,19 @@ module Puppet provider.disable end + newvalue(:manual, :event => :service_manual_start) do + provider.manual_start + end + def retrieve provider.enabled? end + + validate do |value| + if value == :manual and !Puppet.features.microsoft_windows? + raise Puppet::Error.new("Setting enable to manual is only supported on Microsoft Windows.") + end + end end # Handle whether the service should actually be running right now. diff --git a/lib/puppet/util/adsi.rb b/lib/puppet/util/adsi.rb new file mode 100644 index 000000000..f865743e2 --- /dev/null +++ b/lib/puppet/util/adsi.rb @@ -0,0 +1,278 @@ +module Puppet::Util::ADSI + class << self + def connectable?(uri) + begin + !! connect(uri) + rescue + false + end + end + + def connect(uri) + begin + WIN32OLE.connect(uri) + rescue Exception => e + raise Puppet::Error.new( "ADSI connection error: #{e}" ) + end + end + + def create(name, resource_type) + Puppet::Util::ADSI.connect(computer_uri).Create(resource_type, name) + end + + def delete(name, resource_type) + Puppet::Util::ADSI.connect(computer_uri).Delete(resource_type, name) + end + + def computer_name + unless @computer_name + buf = " " * 128 + Win32API.new('kernel32', 'GetComputerName', ['P','P'], 'I').call(buf, buf.length.to_s) + @computer_name = buf.unpack("A*") + end + @computer_name + end + + def computer_uri + "WinNT://#{computer_name}" + end + + def wmi_resource_uri( host = '.' ) + "winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2" + end + + def uri(resource_name, resource_type) + "#{computer_uri}/#{resource_name},#{resource_type}" + end + + def execquery(query) + connect(wmi_resource_uri).execquery(query) + end + end + + class User + extend Enumerable + + attr_accessor :native_user + attr_reader :name + def initialize(name, native_user = nil) + @name = name + @native_user = native_user + end + + def native_user + @native_user ||= Puppet::Util::ADSI.connect(uri) + end + + def self.uri(name) + Puppet::Util::ADSI.uri(name, 'user') + end + + def uri + self.class.uri(name) + end + + def self.logon(name, password) + fLOGON32_LOGON_NETWORK = 3 + fLOGON32_PROVIDER_DEFAULT = 0 + + logon_user = Win32API.new("advapi32", "LogonUser", ['P', 'P', 'P', 'L', 'L', 'P'], 'L') + close_handle = Win32API.new("kernel32", "CloseHandle", ['P'], 'V') + + token = ' ' * 4 + if logon_user.call(name, "", password, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token) != 0 + close_handle.call(token.unpack('L')[0]) + true + else + false + end + end + + def [](attribute) + native_user.Get(attribute) + end + + def []=(attribute, value) + native_user.Put(attribute, value) + end + + def commit + begin + native_user.SetInfo unless native_user.nil? + rescue Exception => e + raise Puppet::Error.new( "User update failed: #{e}" ) + end + self + end + + def password_is?(password) + self.class.logon(name, password) + end + + def add_flag(flag_name, value) + flag = native_user.Get(flag_name) rescue 0 + + native_user.Put(flag_name, flag | value) + + commit + end + + def password=(password) + native_user.SetPassword(password) + commit + fADS_UF_DONT_EXPIRE_PASSWD = 0x10000 + add_flag("UserFlags", fADS_UF_DONT_EXPIRE_PASSWD) + end + + def groups + # WIN32OLE objects aren't enumerable, so no map + groups = [] + native_user.Groups.each {|g| groups << g.Name} + groups + end + + def add_to_groups(*group_names) + group_names.each do |group_name| + Puppet::Util::ADSI::Group.new(group_name).add_member(@name) + end + end + alias add_to_group add_to_groups + + def remove_from_groups(*group_names) + group_names.each do |group_name| + Puppet::Util::ADSI::Group.new(group_name).remove_member(@name) + end + end + alias remove_from_group remove_from_groups + + def set_groups(desired_groups, minimum = true) + return if desired_groups.nil? or desired_groups.empty? + + desired_groups = desired_groups.split(',').map(&:strip) + + current_groups = self.groups + + # First we add the user to all the groups it should be in but isn't + groups_to_add = desired_groups - current_groups + add_to_groups(*groups_to_add) + + # Then we remove the user from all groups it is in but shouldn't be, if + # that's been requested + groups_to_remove = current_groups - desired_groups + remove_from_groups(*groups_to_remove) unless minimum + end + + def self.create(name) + new(name, Puppet::Util::ADSI.create(name, 'user')) + end + + def self.exists?(name) + Puppet::Util::ADSI::connectable?(User.uri(name)) + end + + def self.delete(name) + Puppet::Util::ADSI.delete(name, 'user') + end + + def self.each(&block) + wql = Puppet::Util::ADSI.execquery("select * from win32_useraccount") + + users = [] + wql.each do |u| + users << new(u.name, u) + end + + users.each(&block) + end + end + + class Group + extend Enumerable + + attr_accessor :native_group + attr_reader :name + def initialize(name, native_group = nil) + @name = name + @native_group = native_group + end + + def uri + self.class.uri(name) + end + + def self.uri(name) + Puppet::Util::ADSI.uri(name, 'group') + end + + def native_group + @native_group ||= Puppet::Util::ADSI.connect(uri) + end + + def commit + begin + native_group.SetInfo unless native_group.nil? + rescue Exception => e + raise Puppet::Error.new( "Group update failed: #{e}" ) + end + self + end + + def add_members(*names) + names.each do |name| + native_group.Add(Puppet::Util::ADSI::User.uri(name)) + end + end + alias add_member add_members + + def remove_members(*names) + names.each do |name| + native_group.Remove(Puppet::Util::ADSI::User.uri(name)) + end + end + alias remove_member remove_members + + def members + # WIN32OLE objects aren't enumerable, so no map + members = [] + native_group.Members.each {|m| members << m.Name} + members + end + + def set_members(desired_members) + return if desired_members.nil? or desired_members.empty? + + current_members = self.members + + # First we add all missing members + members_to_add = desired_members - current_members + add_members(*members_to_add) + + # Then we remove all extra members + members_to_remove = current_members - desired_members + remove_members(*members_to_remove) + end + + def self.create(name) + new(name, Puppet::Util::ADSI.create(name, 'group')) + end + + def self.exists?(name) + Puppet::Util::ADSI.connectable?(Group.uri(name)) + end + + def self.delete(name) + Puppet::Util::ADSI.delete(name, 'group') + end + + def self.each(&block) + wql = Puppet::Util::ADSI.execquery( "select * from win32_group" ) + + groups = [] + wql.each do |g| + groups << new(g.name, g) + end + + groups.each(&block) + end + end +end diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 6537a4a4e..2e8710ab1 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -1,5 +1,4 @@ require 'puppet/util/warnings' -require 'puppet/util/cacher' # Autoload paths, either based on names or all at once. class Puppet::Util::Autoload @@ -7,7 +6,6 @@ class Puppet::Util::Autoload include Puppet::Util include Puppet::Util::Warnings - include Puppet::Util::Cacher include Puppet::Util::Autoload::FileCache @autoloaders = {} diff --git a/lib/puppet/util/cacher.rb b/lib/puppet/util/cacher.rb index 3dddec0d4..136c9973e 100644 --- a/lib/puppet/util/cacher.rb +++ b/lib/puppet/util/cacher.rb @@ -1,25 +1,6 @@ require 'monitor' module Puppet::Util::Cacher - module Expirer - attr_reader :timestamp - - # Cause all cached values to be considered expired. - def expire - @timestamp = Time.now - end - - # Is the provided timestamp earlier than our expiration timestamp? - # If it is, then the associated value is expired. - def dependent_data_expired?(ts) - return false unless timestamp - - timestamp > ts - end - end - - extend Expirer - # Our module has been extended in a class; we can only add the Instance methods, # which become *class* methods in the class. def self.extended(other) @@ -40,27 +21,26 @@ module Puppet::Util::Cacher module ClassMethods # Provide a means of defining an attribute whose value will be cached. # Must provide a block capable of defining the value if it's flushed.. - def cached_attr(name, options = {}, &block) + def cached_attr(name, ttl, &block) init_method = "init_#{name}" define_method(init_method, &block) + set_attr_ttl(name, ttl) + define_method(name) do cached_value(name) end define_method(name.to_s + "=") do |value| # Make sure the cache timestamp is set - cache_timestamp - value_cache.synchronize { value_cache[name] = value } - end - - if ttl = options[:ttl] - set_attr_ttl(name, ttl) + value_cache.synchronize do + value_cache[name] = value + set_expiration(name) + end end end def attr_ttl(name) - return nil unless @attr_ttls @attr_ttls[name] end @@ -72,57 +52,25 @@ module Puppet::Util::Cacher # Methods that get added to instances. module InstanceMethods - - def expire - # Only expire if we have an expirer. This is - # mostly so that we can comfortably handle cases - # like Puppet::Type instances, which use their - # catalog as their expirer, and they often don't - # have a catalog. - if e = expirer - e.expire - end - end - - def expirer - Puppet::Util::Cacher - end - private - def cache_timestamp - @cache_timestamp ||= Time.now - end - def cached_value(name) value_cache.synchronize do - # Allow a nil expirer, in which case we regenerate the value every time. - if expired_by_expirer?(name) - value_cache.clear - @cache_timestamp = Time.now - elsif expired_by_ttl?(name) - value_cache.delete(name) + if value_cache[name].nil? or expired_by_ttl?(name) + value_cache[name] = send("init_#{name}") + set_expiration(name) end - value_cache[name] = send("init_#{name}") unless value_cache.include?(name) value_cache[name] end end - def expired_by_expirer?(name) - if expirer.nil? - return true unless self.class.attr_ttl(name) - end - expirer.dependent_data_expired?(cache_timestamp) - end - def expired_by_ttl?(name) - return false unless self.class.respond_to?(:attr_ttl) - return false unless ttl = self.class.attr_ttl(name) - - @ttl_timestamps ||= {} - @ttl_timestamps[name] ||= Time.now + @attr_expirations[name] < Time.now + end - (Time.now - @ttl_timestamps[name]) > ttl + def set_expiration(name) + @attr_expirations ||= {} + @attr_expirations[name] = Time.now + self.class.attr_ttl(name) end def value_cache diff --git a/lib/puppet/util/rdoc/parser.rb b/lib/puppet/util/rdoc/parser.rb index 762ce25f0..a8996ee9a 100644 --- a/lib/puppet/util/rdoc/parser.rb +++ b/lib/puppet/util/rdoc/parser.rb @@ -113,7 +113,9 @@ class Parser Puppet::Module.modulepath.each do |mp| # check that fullpath is a descendant of mp dirname = fullpath - while (dirname = File.dirname(dirname)) != '/' + previous = dirname + while (dirname = File.dirname(previous)) != previous + previous = dirname return nil if File.identical?(dirname,mp) end end diff --git a/lib/puppet/util/run_mode.rb b/lib/puppet/util/run_mode.rb index 450cbf1a6..6028aef29 100644 --- a/lib/puppet/util/run_mode.rb +++ b/lib/puppet/util/run_mode.rb @@ -27,14 +27,14 @@ module Puppet def conf_dir which_dir( - (Puppet.features.microsoft_windows? ? File.join(Dir::WINDOWS, "puppet", "etc") : "/etc/puppet"), + (Puppet.features.microsoft_windows? ? File.join(Dir::COMMON_APPDATA, "PuppetLabs", "puppet", "etc") : "/etc/puppet"), "~/.puppet" ) end def var_dir which_dir( - (Puppet.features.microsoft_windows? ? File.join(Dir::WINDOWS, "puppet", "var") : "/var/lib/puppet"), + (Puppet.features.microsoft_windows? ? File.join(Dir::COMMON_APPDATA, "PuppetLabs", "puppet", "var") : "/var/lib/puppet"), "~/.puppet/var" ) end diff --git a/lib/puppet/util/settings.rb b/lib/puppet/util/settings.rb index 4559e9af3..caaf61b7b 100644 --- a/lib/puppet/util/settings.rb +++ b/lib/puppet/util/settings.rb @@ -2,13 +2,11 @@ require 'puppet' require 'sync' require 'getoptlong' require 'puppet/external/event-loop' -require 'puppet/util/cacher' require 'puppet/util/loadedfile' # The class for handling configuration files. class Puppet::Util::Settings include Enumerable - include Puppet::Util::Cacher require 'puppet/util/settings/setting' require 'puppet/util/settings/file_setting' @@ -401,11 +399,10 @@ class Puppet::Util::Settings } end - # Cache this in an easily clearable way, since we were - # having trouble cleaning it up after tests. - cached_attr(:file) do + def file + return @file if @file if path = self[:config] and FileTest.exist?(path) - Puppet::Util::LoadedFile.new(path) + @file = Puppet::Util::LoadedFile.new(path) end end diff --git a/lib/puppet/util/settings/file_setting.rb b/lib/puppet/util/settings/file_setting.rb index 776398ef4..0fa65d846 100644 --- a/lib/puppet/util/settings/file_setting.rb +++ b/lib/puppet/util/settings/file_setting.rb @@ -86,7 +86,7 @@ class Puppet::Util::Settings::FileSetting < Puppet::Util::Settings::Setting path = File.expand_path(path) return nil unless type == :directory or create_files? or File.exist?(path) - return nil if path =~ /^\/dev/ + return nil if path =~ /^\/dev/ or path =~ /^[A-Z]:\/dev/i resource = Puppet::Resource.new(:file, path) |