summaryrefslogtreecommitdiffstats
path: root/lib/puppet
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet')
-rw-r--r--lib/puppet/application/master.rb2
-rw-r--r--lib/puppet/configurer.rb4
-rw-r--r--lib/puppet/defaults.rb16
-rw-r--r--lib/puppet/feature/base.rb14
-rw-r--r--lib/puppet/file_bucket/dipper.rb5
-rw-r--r--lib/puppet/file_bucket/file.rb6
-rw-r--r--lib/puppet/file_serving/base.rb5
-rw-r--r--lib/puppet/file_serving/configuration.rb16
-rw-r--r--lib/puppet/file_serving/fileset.rb9
-rw-r--r--lib/puppet/file_serving/indirection_hooks.rb1
-rw-r--r--lib/puppet/file_serving/mount.rb1
-rw-r--r--lib/puppet/file_serving/mount/file.rb21
-rw-r--r--lib/puppet/indirector/facts/facter.rb4
-rw-r--r--lib/puppet/indirector/file_server.rb2
-rw-r--r--lib/puppet/indirector/indirection.rb8
-rw-r--r--lib/puppet/indirector/request.rb4
-rw-r--r--lib/puppet/network/client.rb5
-rw-r--r--lib/puppet/network/http_pool.rb56
-rw-r--r--lib/puppet/node/environment.rb20
-rw-r--r--lib/puppet/parameter.rb6
-rw-r--r--lib/puppet/parser/type_loader.rb3
-rw-r--r--lib/puppet/provider/group/windows_adsi.rb48
-rw-r--r--lib/puppet/provider/host/parsed.rb3
-rw-r--r--lib/puppet/provider/service/windows.rb110
-rw-r--r--lib/puppet/provider/user/windows_adsi.rb71
-rw-r--r--lib/puppet/resource/catalog.rb20
-rw-r--r--lib/puppet/ssl/certificate_authority.rb12
-rw-r--r--lib/puppet/ssl/host.rb13
-rw-r--r--lib/puppet/type.rb8
-rw-r--r--lib/puppet/type/file.rb43
-rwxr-xr-xlib/puppet/type/file/source.rb23
-rw-r--r--lib/puppet/type/service.rb10
-rw-r--r--lib/puppet/util/adsi.rb278
-rw-r--r--lib/puppet/util/autoload.rb2
-rw-r--r--lib/puppet/util/cacher.rb82
-rw-r--r--lib/puppet/util/rdoc/parser.rb4
-rw-r--r--lib/puppet/util/run_mode.rb4
-rw-r--r--lib/puppet/util/settings.rb9
-rw-r--r--lib/puppet/util/settings/file_setting.rb2
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)