diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb index d5e06c54a..e6beb512e 100644 --- a/lib/puppet/defaults.rb +++ b/lib/puppet/defaults.rb @@ -1,860 +1,862 @@ # The majority of the system configuration parameters are set in this file. module Puppet setdefaults(:main, :confdir => [Puppet.run_mode.conf_dir, "The main Puppet configuration directory. The default for this parameter is calculated based on the user. If the process is running as root or the user that Puppet is supposed to run as, it defaults to a system directory, but if it's running as any other user, it defaults to being in the user's home directory."], :vardir => [Puppet.run_mode.var_dir, "Where Puppet stores dynamic and growing data. The default for this parameter is calculated specially, like `confdir`_."], :name => [Puppet.application_name.to_s, "The name of the application, if we are running as one. The default is essentially $0 without the path or `.rb`."], :run_mode => [Puppet.run_mode.name.to_s, "The effective 'run mode' of the application: master, agent, or user."] ) setdefaults(:main, :logdir => Puppet.run_mode.logopts) setdefaults(:main, :trace => [false, "Whether to print stack traces on some errors"], :autoflush => { :default => false, :desc => "Whether log files should always flush to disk.", :hook => proc { |value| Log.autoflush = value } }, :syslogfacility => ["daemon", "What syslog facility to use when logging to syslog. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up."], :statedir => { :default => "$vardir/state", :mode => 01755, :desc => "The directory where Puppet state is stored. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts)." }, :rundir => { :default => Puppet.run_mode.run_dir, :mode => 01777, :desc => "Where Puppet PID files are kept." }, :genconfig => [false, "Whether to just print a configuration to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI."], :genmanifest => [false, "Whether to just print a manifest to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI."], :configprint => ["", "Print the value of a specific configuration parameter. If a parameter is provided for this, then the value is printed and 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 => { :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.", }, :mkusers => [false, "Whether to create the necessary user and group that puppet agent will run as."], :manage_internal_file_permissions => [true, "Whether Puppet should manage the owner, group, and mode of files it uses internally" ], :onetime => {:default => false, :desc => "Run the configuration once, rather than as a long-running daemon. This is useful for interactively running puppetd.", :short => 'o' }, :path => {:default => "none", :desc => "The shell search path. Defaults to whatever is inherited from the parent process.", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| ENV["PATH"] = "" if ENV["PATH"].nil? ENV["PATH"] = value unless value == "none" paths = ENV["PATH"].split(File::PATH_SEPARATOR) %w{/usr/sbin /sbin}.each do |path| ENV["PATH"] += File::PATH_SEPARATOR + path unless paths.include?(path) end value end }, :libdir => {:default => "$vardir/lib", :desc => "An extra search path for Puppet. This is only useful for those files that Puppet will load on demand, and is only guaranteed to work for those cases. In fact, the autoload mechanism is responsible for making sure this directory is in Ruby's search path", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| $LOAD_PATH.delete(@oldlibdir) if defined?(@oldlibdir) and $LOAD_PATH.include?(@oldlibdir) @oldlibdir = value $LOAD_PATH << value end }, :ignoreimport => [false, "A parameter that can be used in commit hooks, since it enables you to parse-check a single file rather than requiring that all files exist."], :authconfig => [ "$confdir/namespaceauth.conf", "The configuration file that defines the rights to the different namespaces and methods. This can be used as a coarse-grained authorization system for both `puppet agent` and `puppet master`." ], :environment => {:default => "production", :desc => "The environment Puppet is running in. For clients (e.g., `puppet agent`) this determines the environment itself, which is used to find modules and much more. For servers (i.e., `puppet master`) this provides the default environment for nodes we know nothing about." }, :diff_args => ["-u", "Which arguments to pass to the diff command when printing differences between files."], :diff => ["diff", "Which diff command to use when printing differences between files."], :show_diff => [false, "Whether to print a contextual diff when files are being replaced. The diff is printed on stdout, so this option is meaningless unless you are running Puppet interactively. This feature currently requires the `diff/lcs` Ruby library."], :daemonize => { :default => true, :desc => "Send the process into the background. This is the default.", :short => "D" }, :maximum_uid => [4294967290, "The maximum allowed UID. Some platforms use negative UIDs but then ship with tools that do not know how to handle signed ints, so the UIDs show up as huge numbers that can then not be fed back into the system. This is a hackish way to fail in a slightly more useful way when that happens."], :route_file => ["$confdir/routes.yaml", "The YAML file containing indirector route configuration."], :node_terminus => ["plain", "Where to find information about nodes."], :catalog_terminus => ["compiler", "Where to get node catalogs. This is useful to change if, for instance, you'd like to pre-compile catalogs and store them in memcached or some other easily-accessed store."], :facts_terminus => { :default => Puppet.application_name.to_s == "master" ? 'yaml' : 'facter', :desc => "The node facts terminus.", :hook => proc do |value| require 'puppet/node/facts' if value.to_s == "rest" Puppet::Node::Facts.indirection.cache_class = :yaml end end }, :inventory_terminus => [ "$facts_terminus", "Should usually be the same as the facts terminus" ], :httplog => { :default => "$logdir/http.log", :owner => "root", :mode => 0640, :desc => "Where the puppet agent web server logs." }, :http_proxy_host => ["none", "The HTTP proxy host to use for outgoing connections. Note: You may need to use a FQDN for the server hostname when using a proxy."], :http_proxy_port => [3128, "The HTTP proxy port to use for outgoing connections"], :filetimeout => [ 15, "The minimum time to wait (in seconds) between checking for updates in configuration files. This timeout determines how quickly Puppet checks whether a file (such as manifests or templates) has changed on disk." ], :queue_type => ["stomp", "Which type of queue to use for asynchronous processing."], :queue_type => ["stomp", "Which type of queue to use for asynchronous processing."], :queue_source => ["stomp://localhost:61613/", "Which type of queue to use for asynchronous processing. If your stomp server requires authentication, you can include it in the URI as long as your stomp client library is at least 1.1.1"], :async_storeconfigs => {:default => false, :desc => "Whether to use a queueing system to provide asynchronous database integration. Requires that `puppetqd` be running and that 'PSON' support for ruby be installed.", :hook => proc do |value| if value # This reconfigures the terminii for Node, Facts, and Catalog Puppet.settings[:storeconfigs] = true # But then we modify the configuration Puppet::Resource::Catalog.indirection.cache_class = :queue else raise "Cannot disable asynchronous storeconfigs in a running process" end end }, :thin_storeconfigs => {:default => false, :desc => "Boolean; wether storeconfigs store in the database only the facts and exported resources. If true, then storeconfigs performance will be higher and still allow exported/collected resources, but other usage external to Puppet might not work", :hook => proc do |value| Puppet.settings[:storeconfigs] = true if value end }, :config_version => ["", "How to determine the configuration version. By default, it will be the time that the configuration is parsed, but you can provide a shell script to override how the version is determined. The output of this script will be added to every log message in the reports, allowing you to correlate changes on your hosts to the source version on the server."], :zlib => [true, "Boolean; whether to use the zlib library", ], :prerun_command => ["", "A command to run before every agent run. If this command returns a non-zero return code, the entire Puppet run will fail."], :postrun_command => ["", "A command to run after every agent run. If this command returns a non-zero return code, the entire Puppet run will be considered to have failed, even though it might have performed work during the normal run."], :freeze_main => [false, "Freezes the 'main' class, disallowing any code to be added to it. This essentially means that you can't have any code outside of a node, class, or definition other than in the site manifest."] ) hostname = Facter["hostname"].value domain = Facter["domain"].value if domain and domain != "" fqdn = [hostname, domain].join(".") else fqdn = hostname end Puppet.setdefaults( :main, # We have to downcase the fqdn, because the current ssl stuff (as oppsed to in master) doesn't have good facilities for # manipulating naming. :certname => {:default => fqdn.downcase, :desc => "The name to use when handling certificates. Defaults to the fully qualified domain name.", :call_on_define => true, # Call our hook with the default value, so we're always downcased :hook => proc { |value| raise(ArgumentError, "Certificate names must be lower case; see #1168") unless value == value.downcase }}, :certdnsnames => ['', "The DNS names on the Server certificate as a colon-separated list. If it's anything other than an empty string, it will be used as an alias in the created certificate. By default, only the server gets an alias set up, and only for 'puppet'."], :certdir => { :default => "$ssldir/certs", :owner => "service", :desc => "The certificate directory." }, :ssldir => { :default => "$confdir/ssl", :mode => 0771, :owner => "service", :desc => "Where SSL certificates are kept." }, :publickeydir => { :default => "$ssldir/public_keys", :owner => "service", :desc => "The public key directory." }, :requestdir => { :default => "$ssldir/certificate_requests", :owner => "service", :desc => "Where host certificate requests are stored." }, :privatekeydir => { :default => "$ssldir/private_keys", :mode => 0750, :owner => "service", :desc => "The private key directory." }, :privatedir => { :default => "$ssldir/private", :mode => 0750, :owner => "service", :desc => "Where the client stores private certificate information." }, :passfile => { :default => "$privatedir/password", :mode => 0640, :owner => "service", :desc => "Where puppet agent stores the password for its private key. Generally unused." }, :hostcsr => { :default => "$ssldir/csr_$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificate requests." }, :hostcert => { :default => "$certdir/$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their certificates." }, :hostprivkey => { :default => "$privatekeydir/$certname.pem", :mode => 0600, :owner => "service", :desc => "Where individual hosts store and look for their private key." }, :hostpubkey => { :default => "$publickeydir/$certname.pem", :mode => 0644, :owner => "service", :desc => "Where individual hosts store and look for their public key." }, :localcacert => { :default => "$certdir/ca.pem", :mode => 0644, :owner => "service", :desc => "Where each client stores the CA certificate." }, :hostcrl => { :default => "$ssldir/crl.pem", :mode => 0644, :owner => "service", :desc => "Where the host's certificate revocation list can be found. This is distinct from the certificate authority's CRL." }, :certificate_revocation => [true, "Whether certificate revocation should be supported by downloading a Certificate Revocation List (CRL) to all clients. If enabled, CA chaining will almost definitely not work."] ) setdefaults( :ca, :ca_name => ["Puppet CA: $certname", "The name to use the Certificate Authority certificate."], :cadir => { :default => "$ssldir/ca", :owner => "service", :group => "service", :mode => 0770, :desc => "The root directory for the certificate authority." }, :cacert => { :default => "$cadir/ca_crt.pem", :owner => "service", :group => "service", :mode => 0660, :desc => "The CA certificate." }, :cakey => { :default => "$cadir/ca_key.pem", :owner => "service", :group => "service", :mode => 0660, :desc => "The CA private key." }, :capub => { :default => "$cadir/ca_pub.pem", :owner => "service", :group => "service", :desc => "The CA public key." }, :cacrl => { :default => "$cadir/ca_crl.pem", :owner => "service", :group => "service", :mode => 0664, :desc => "The certificate revocation list (CRL) for the CA. Will be used if present but otherwise ignored.", :hook => proc do |value| if value == 'false' Puppet.warning "Setting the :cacrl to 'false' is deprecated; Puppet will just ignore the crl if yours is missing" end end }, :caprivatedir => { :default => "$cadir/private", :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores private certificate information." }, :csrdir => { :default => "$cadir/requests", :owner => "service", :group => "service", :desc => "Where the CA stores certificate requests" }, :signeddir => { :default => "$cadir/signed", :owner => "service", :group => "service", :mode => 0770, :desc => "Where the CA stores signed certificates." }, :capass => { :default => "$caprivatedir/ca.pass", :owner => "service", :group => "service", :mode => 0660, :desc => "Where the CA stores the password for the private key" }, :serial => { :default => "$cadir/serial", :owner => "service", :group => "service", :mode => 0644, :desc => "Where the serial number for certificates is stored." }, :autosign => { :default => "$confdir/autosign.conf", :mode => 0644, :desc => "Whether to enable autosign. Valid values are true (which autosigns any key request, and is a very bad idea), false (which never autosigns any key request), and the path to a file, which uses that configuration file to determine which keys to sign."}, :allow_duplicate_certs => [false, "Whether to allow a new certificate request to overwrite an existing certificate."], :ca_days => ["", "How long a certificate should be valid. This parameter is deprecated, use ca_ttl instead"], :ca_ttl => ["5y", "The default TTL for new certificates; valid values must be an integer, optionally followed by one of the units 'y' (years of 365 days), 'd' (days), 'h' (hours), or 's' (seconds). The unit defaults to seconds. If this parameter is set, ca_days is ignored. Examples are '3600' (one hour) and '1825d', which is the same as '5y' (5 years) "], :ca_md => ["md5", "The type of hash used in certificates."], :req_bits => [2048, "The bit length of the certificates."], :keylength => [1024, "The bit length of keys."], :cert_inventory => { :default => "$cadir/inventory.txt", :mode => 0644, :owner => "service", :group => "service", :desc => "A Complete listing of all certificates" } ) # Define the config default. setdefaults( Puppet.settings[:name], :config => ["$confdir/puppet.conf", "The configuration file for #{Puppet[:name]}."], :pidfile => ["$rundir/$name.pid", "The pid file"], :bindaddress => ["", "The address a listening server should bind to. Mongrel servers default to 127.0.0.1 and WEBrick defaults to 0.0.0.0."], :servertype => {:default => "webrick", :desc => "The type of server to use. Currently supported options are webrick and mongrel. If you use mongrel, you will need a proxy in front of the process or processes, since Mongrel cannot speak SSL.", :call_on_define => true, # Call our hook with the default value, so we always get the correct bind address set. :hook => proc { |value| value == "webrick" ? Puppet.settings[:bindaddress] = "0.0.0.0" : Puppet.settings[:bindaddress] = "127.0.0.1" if Puppet.settings[:bindaddress] == "" } } ) setdefaults(:master, :user => ["puppet", "The user puppet master should run as."], :group => ["puppet", "The group puppet master should run as."], :manifestdir => ["$confdir/manifests", "Where puppet master looks for its manifests."], :manifest => ["$manifestdir/site.pp", "The entry-point manifest for puppet master."], :code => ["", "Code to parse directly. This is essentially only used by `puppet`, and should only be set if you're writing your own Puppet executable"], :masterlog => { :default => "$logdir/puppetmaster.log", :owner => "service", :group => "service", :mode => 0660, :desc => "Where puppet master logs. This is generally not used, since syslog is the default log destination." }, :masterhttplog => { :default => "$logdir/masterhttp.log", :owner => "service", :group => "service", :mode => 0660, :create => true, :desc => "Where the puppet master web server logs." }, :masterport => [8140, "Which port puppet master listens on."], :node_name => ["cert", "How the puppet master determines the client's identity and sets the 'hostname', 'fqdn' and 'domain' facts for use in the manifest, in particular for determining which 'node' statement applies to the client. Possible values are 'cert' (use the subject's CN in the client's certificate) and 'facter' (use the hostname that the client reported in its facts)"], :bucketdir => { :default => "$vardir/bucket", :mode => 0750, :owner => "service", :group => "service", :desc => "Where FileBucket files are stored." }, :rest_authconfig => [ "$confdir/auth.conf", "The configuration file that defines the rights to the different rest indirections. This can be used as a fine-grained 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`). See http://projects.puppetlabs.com/projects/puppet/wiki/Using_Mongrel for more information."], :ssl_client_verify_header => ["HTTP_X_CLIENT_VERIFY", "The header containing the status message of the client verification. Only used with Mongrel. This header must be set by the proxy to 'SUCCESS' if the client successfully authenticated, and anything else otherwise. See http://projects.puppetlabs.com/projects/puppet/wiki/Using_Mongrel for more information."], # To make sure this directory is created before we try to use it on the server, we need # it to be in the server section (#1138). :yamldir => {:default => "$vardir/yaml", :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which YAML data is stored, usually in a subdirectory."}, :server_datadir => {:default => "$vardir/server_data", :owner => "service", :group => "service", :mode => "750", :desc => "The directory in which serialized data is stored, usually in a subdirectory."}, :reports => ["store", "The list of reports to generate. All reports are looked for in `puppet/reports/name.rb`, and multiple report names should be comma-separated (whitespace is okay)." ], :reportdir => {:default => "$vardir/reports", :mode => 0750, :owner => "service", :group => "service", :desc => "The directory in which to store reports received from the client. Each client gets a separate subdirectory."}, :reporturl => ["http://localhost:3000/reports", "The URL used by the http reports processor to send reports"], :fileserverconfig => ["$confdir/fileserver.conf", "Where the fileserver configuration is stored."], :strict_hostname_checking => [false, "Whether to only search for the complete hostname as it is in the certificate when searching for node information in the catalogs."] ) setdefaults(:metrics, :rrddir => {:default => "$vardir/rrd", :mode => 0750, :owner => "service", :group => "service", :desc => "The directory where RRD database files are stored. Directories for each reporting host will be created under this directory." }, :rrdinterval => ["$runinterval", "How often RRD should expect data. This should match how often the hosts report back to the server."] ) setdefaults(:device, :devicedir => {:default => "$vardir/devices", :mode => "750", :desc => "The root directory of devices' $vardir"}, :deviceconfig => ["$confdir/device.conf","Path to the device config file for puppet device"] ) setdefaults(:agent, :node_name_value => { :default => "$certname", :desc => "The explicit value used for the node name for all requests the agent makes to the master. WARNING: This setting is mutually exclusive with node_name_fact. Changing this setting also requires changes to the default auth.conf configuration on the Puppet Master. Please see http://links.puppetlabs.com/node_name_value for more information." }, :node_name_fact => { :default => "", :desc => "The fact name used to determine the node name used for all requests the agent makes to the master. WARNING: This setting is mutually exclusive with node_name_value. Changing this setting also requires changes to the default auth.conf configuration on the Puppet Master. Please see http://links.puppetlabs.com/node_name_fact for more information.", :hook => proc do |value| if !value.empty? and Puppet[:node_name_value] != Puppet[:certname] raise "Cannot specify both the node_name_value and node_name_fact settings" end end }, :localconfig => { :default => "$statedir/localconfig", :owner => "root", :mode => 0660, :desc => "Where puppet agent caches the local configuration. An extension indicating the cache format is added automatically."}, :statefile => { :default => "$statedir/state.yaml", :mode => 0660, :desc => "Where puppet agent and puppet master store state associated with the running configuration. In the case of puppet master, this file reflects the state discovered through interacting with clients." }, :clientyamldir => {:default => "$vardir/client_yaml", :mode => "750", :desc => "The directory in which client-side YAML data is stored."}, :client_datadir => {:default => "$vardir/client_data", :mode => "750", :desc => "The directory in which serialized data is stored on the client."}, :classfile => { :default => "$statedir/classes.txt", :owner => "root", :mode => 0644, :desc => "The file in which puppet agent stores a list of the classes associated with the retrieved configuration. Can be loaded in the separate `puppet` executable using the `--loadclasses` option."}, :puppetdlog => { :default => "$logdir/puppetd.log", :owner => "root", :mode => 0640, :desc => "The log file for puppet agent. This is generally not used." }, :server => ["puppet", "The server to which server puppet agent should connect"], :ignoreschedules => [false, "Boolean; whether puppet agent should ignore schedules. This is useful for initial puppet agent runs."], :puppetport => [8139, "Which port puppet agent listens on."], :noop => [false, "Whether puppet agent should be run in noop mode."], :runinterval => [1800, # 30 minutes "How often puppet agent applies the client configuration; in seconds."], :listen => [false, "Whether puppet agent should listen for connections. If this is true, then puppet agent will accept incoming REST API requests, subject to the default ACLs and the ACLs set in the `rest_authconfig` file. Puppet agent can respond usefully to requests on the `run`, `facts`, `certificate`, and `resource` endpoints."], :ca_server => ["$server", "The server to use for certificate authority requests. It's a separate server because it cannot and does not need to horizontally scale."], :ca_port => ["$masterport", "The port to use for the certificate authority."], :catalog_format => { :default => "", :desc => "(Deprecated for 'preferred_serialization_format') What format to use to dump the catalog. Only supports 'marshal' and 'yaml'. Only matters on the client, since it asks the server for a specific format.", :hook => proc { |value| if value Puppet.warning "Setting 'catalog_format' is deprecated; use 'preferred_serialization_format' instead." Puppet.settings[:preferred_serialization_format] = value end } }, :preferred_serialization_format => ["pson", "The preferred means of serializing ruby instances for passing over the wire. This won't guarantee that all instances will be serialized using this method, since not all classes can be guaranteed to support this format, but it will be used for all classes that support it."], :puppetdlockfile => [ "$statedir/puppetdlock", "A lock file to temporarily stop puppet agent from doing anything."], :usecacheonfailure => [true, "Whether to use the cached configuration when the remote configuration will not compile. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known-good one." ], :use_cached_catalog => [false, "Whether to only use the cached catalog rather than compiling a new catalog on every run. Puppet can be run with this enabled by default and then selectively disabled when a recompile is desired."], :ignorecache => [false, "Ignore cache and always recompile the configuration. This is useful for testing new configurations, where the local cache may in fact be stale even if the timestamps are up to date - if the facts change or if the server changes." ], :downcasefacts => [false, "Whether facts should be made all lowercase when sent to the server."], :dynamicfacts => ["memorysize,memoryfree,swapsize,swapfree", "Facts that are dynamic; these facts will be ignored when deciding whether changed facts should result in a recompile. Multiple facts should be comma-separated."], :splaylimit => ["$runinterval", "The maximum time to delay before runs. Defaults to being the same as the run interval."], :splay => [false, "Whether to sleep for a pseudo-random (but consistent) amount of time before a run."], :clientbucketdir => { :default => "$vardir/clientbucket", :mode => 0750, :desc => "Where FileBucket files are stored locally." }, :configtimeout => [120, "How long the client should wait for the configuration to be retrieved before considering it a failure. This can help reduce flapping if too many clients contact the server at one time." ], :reportserver => { :default => "$server", :call_on_define => false, :desc => "(Deprecated for 'report_server') The server to which to send transaction reports.", :hook => proc do |value| Puppet.settings[:report_server] = value if value end }, :report_server => ["$server", "The server to send transaction reports to." ], :report_port => ["$masterport", "The port to communicate with the report_server." ], :inventory_server => ["$server", "The server to send facts to." ], :inventory_port => ["$masterport", "The port to communicate with the inventory_server." ], :report => [true, "Whether to send reports after every transaction." ], :lastrunfile => { :default => "$statedir/last_run_summary.yaml", :mode => 0660, :desc => "Where puppet agent stores the last run report summary in yaml format." }, :lastrunreport => { :default => "$statedir/last_run_report.yaml", :mode => 0660, :desc => "Where puppet agent stores the last run report in yaml format." }, :graph => [false, "Whether to create dot graph files for the different configuration graphs. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick)."], :graphdir => ["$statedir/graphs", "Where to store dot-outputted graphs."], :http_compression => [false, "Allow http compression in REST communication with the master. This setting might improve performance for agent -> master communications over slow WANs. Your puppet master needs to support compression (usually by activating some settings in a reverse-proxy in front of the puppet master, which rules out webrick). It is harmless to activate this settings if your master doesn't support compression, but if it supports it, this setting might reduce performance on high-speed LANs."] ) setdefaults(:inspect, :archive_files => [false, "During an inspect run, whether to archive files whose contents are audited to a file bucket."], :archive_file_server => ["$server", "During an inspect run, the file bucket server to archive files to if archive_files is set."] ) # Plugin information. setdefaults( :main, :plugindest => ["$libdir", "Where Puppet should store plugins that it pulls down from the central server."], :pluginsource => ["puppet://$server/plugins", "From where to retrieve plugins. The standard Puppet `file` type is used for retrieval, so anything that is a valid file source can be used here."], :pluginsync => [false, "Whether plugins should be synced with the central server."], :pluginsignore => [".svn CVS .git", "What files to ignore when pulling down plugins."] ) # Central fact information. setdefaults( :main, :factpath => {:default => "$vardir/lib/facter:$vardir/facts", :desc => "Where Puppet should look for facts. Multiple directories should be colon-separated, like normal PATH variables.", :call_on_define => true, # Call our hook with the default value, so we always get the value added to facter. :type => :setting, # Don't consider it a file, because it could be multiple colon-separated files :hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }}, :factdest => ["$vardir/facts/", "Where Puppet should store facts that it pulls down from the central server."], :factsource => ["puppet://$server/facts/", "From where to retrieve facts. The standard Puppet `file` type is used for retrieval, so anything that is a valid file source can be used here."], :factsync => [false, "Whether facts should be synced with the central server."], :factsignore => [".svn CVS", "What files to ignore when pulling down facts."] ) setdefaults( :tagmail, :tagmap => ["$confdir/tagmail.conf", "The mapping between reporting tags and email addresses."], :sendmail => [which('sendmail') || '', "Where to find the sendmail binary with which to send email."], :reportfrom => ["report@" + [Facter["hostname"].value, Facter["domain"].value].join("."), "The 'from' email address for the reports."], :smtpserver => ["none", "The server through which to send email reports."] ) setdefaults( :rails, :dblocation => { :default => "$statedir/clientconfigs.sqlite3", :mode => 0660, :owner => "service", :group => "service", :desc => "The database cache for client configurations. Used for querying within the language." }, :dbadapter => [ "sqlite3", "The type of database to use." ], :dbmigrate => [ false, "Whether to automatically migrate the database." ], :dbname => [ "puppet", "The name of the database to use." ], :dbserver => [ "localhost", "The database server for caching. Only used when networked databases are used."], :dbport => [ "", "The database password for caching. Only used when networked databases are used."], :dbuser => [ "puppet", "The database user for caching. Only used when networked databases are used."], :dbpassword => [ "puppet", "The database password for caching. Only used when networked databases are used."], :dbconnections => [ '', "The number of database connections for networked databases. Will be ignored unless the value is a positive integer."], :dbsocket => [ "", "The database socket location. Only used when networked databases are used. Will be ignored if the value is an empty string."], :railslog => {:default => "$logdir/rails.log", :mode => 0600, :owner => "service", :group => "service", :desc => "Where Rails-specific logs are sent" }, :rails_loglevel => ["info", "The log level for Rails connections. The value must be a valid log level within Rails. Production environments normally use `info` and other environments normally use `debug`."] ) setdefaults( :couchdb, :couchdb_url => ["http://127.0.0.1:5984/puppet", "The url where the puppet couchdb database will be created"] ) setdefaults( :transaction, :tags => ["", "Tags to use to find resources. If this is set, then only resources tagged with the specified tags will be applied. Values must be comma-separated."], :evaltrace => [false, "Whether each resource should log when it is being evaluated. This allows you to interactively see exactly what is being done."], :summarize => [false, "Whether to print a transaction summary." ] ) setdefaults( :main, :external_nodes => ["none", "An external command that can produce node information. The output must be a YAML dump of a hash, and that hash must have one or both of `classes` and `parameters`, where `classes` is an array and `parameters` is a hash. For unknown nodes, the commands should exit with a non-zero exit code. This command makes it straightforward to store your node mapping information in other data sources like databases."]) setdefaults( :ldap, :ldapnodes => [false, "Whether to search for node configurations in LDAP. See http://projects.puppetlabs.com/projects/puppet/wiki/LDAP_Nodes for more information."], :ldapssl => [false, "Whether SSL should be used when searching for nodes. Defaults to false because SSL usually requires certificates to be set up on the client side."], :ldaptls => [false, "Whether TLS should be used when searching for nodes. Defaults to false because TLS usually requires certificates to be set up on the client side."], :ldapserver => ["ldap", "The LDAP server. Only used if `ldapnodes` is enabled."], :ldapport => [389, "The LDAP port. Only used if `ldapnodes` is enabled."], :ldapstring => ["(&(objectclass=puppetClient)(cn=%s))", "The search string used to find an LDAP node."], :ldapclassattrs => ["puppetclass", "The LDAP attributes to use to define Puppet classes. Values should be comma-separated."], :ldapstackedattrs => ["puppetvar", "The LDAP attributes that should be stacked to arrays by adding the values in all hierarchy elements of the tree. Values should be comma-separated."], :ldapattrs => ["all", "The LDAP attributes to include when querying LDAP for nodes. All returned attributes are set as variables in the top-level scope. Multiple values should be comma-separated. The value 'all' returns all attributes."], :ldapparentattr => ["parentnode", "The attribute to use to define the parent node."], :ldapuser => ["", "The user to use to connect to LDAP. Must be specified as a full DN."], :ldappassword => ["", "The password to use to connect to LDAP."], :ldapbase => ["", "The search base for LDAP searches. It's impossible to provide a meaningful default here, although the LDAP libraries might have one already set. Generally, it should be the 'ou=Hosts' branch under your main directory."] ) setdefaults(:master, :storeconfigs => {:default => false, :desc => "Whether to store each client's configuration. This requires ActiveRecord from Ruby on Rails.", :call_on_define => true, # Call our hook with the default value, so we always get the libdir set. :hook => proc do |value| require 'puppet/node' require 'puppet/node/facts' if value require 'puppet/rails' raise "StoreConfigs not supported without ActiveRecord 2.1 or higher" unless Puppet.features.rails? Puppet::Resource::Catalog.indirection.cache_class = :active_record unless Puppet.settings[:async_storeconfigs] Puppet::Node::Facts.indirection.cache_class = :active_record Puppet::Node.indirection.cache_class = :active_record end end } ) # This doesn't actually work right now. setdefaults( :parser, :lexical => [false, "Whether to use lexical scoping (vs. dynamic)."], :templatedir => ["$vardir/templates", "Where Puppet looks for template files. Can be a list of colon-seperated directories." ] ) setdefaults( :puppetdoc, :document_all => [false, "Document all resources"] ) end diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb index f29f70a53..b4f1457df 100644 --- a/lib/puppet/file_serving/fileset.rb +++ b/lib/puppet/file_serving/fileset.rb @@ -1,172 +1,177 @@ # # Created by Luke Kanies on 2007-10-22. # Copyright (c) 2007. All rights reserved. require 'find' require 'puppet/file_serving' require 'puppet/file_serving/metadata' # Operate recursively on a path, returning a set of file paths. class Puppet::FileServing::Fileset attr_reader :path, :ignore, :links attr_accessor :recurse, :recurselimit, :checksum_type # Produce a hash of files, with merged so that earlier files # with the same postfix win. E.g., /dir1/subfile beats /dir2/subfile. # It's a hash because we need to know the relative path of each file, # and the base directory. # This will probably only ever be used for searching for plugins. def self.merge(*filesets) result = {} filesets.each do |fileset| fileset.files.each do |file| result[file] ||= fileset.path end end result end # Return a list of all files in our fileset. This is different from the # normal definition of find in that we support specific levels # of recursion, which means we need to know when we're going another # level deep, which Find doesn't do. def files files = perform_recursion # Now strip off the leading path, so each file becomes relative, and remove # any slashes that might end up at the beginning of the path. result = files.collect { |file| file.sub(%r{^#{Regexp.escape(@path)}/*}, '') } # And add the path itself. result.unshift(".") result end # Should we ignore this path? def ignore?(path) return false if @ignore == [nil] # 'detect' normally returns the found result, whereas we just want true/false. ! @ignore.detect { |pattern| File.fnmatch?(pattern, path) }.nil? end def ignore=(values) values = [values] unless values.is_a?(Array) @ignore = values 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 # Set our defaults. @ignore = [] @links = :manage @recurse = false @recurselimit = :infinite if options.is_a?(Puppet::Indirector::Request) initialize_from_request(options) else initialize_from_hash(options) end raise ArgumentError.new("Fileset paths must exist") unless stat = stat(path) raise ArgumentError.new("Fileset recurse parameter must not be a number anymore, please use recurselimit") if @recurse.is_a?(Integer) end def links=(links) links = links.to_sym raise(ArgumentError, "Invalid :links value '#{links}'") unless [:manage, :follow].include?(links) @links = links @stat_method = links == :manage ? :lstat : :stat end # Should we recurse further? This is basically a single # place for all of the logic around recursion. def recurse?(depth) # recurse if told to, and infinite recursion or current depth not at the limit self.recurse and (self.recurselimit == :infinite or depth <= self.recurselimit) end def initialize_from_hash(options) options.each do |option, value| method = option.to_s + "=" begin send(method, value) rescue NoMethodError raise ArgumentError, "Invalid option '#{option}'" end end end def initialize_from_request(request) [:links, :ignore, :recurse, :recurselimit, :checksum_type].each do |param| if request.options.include?(param) # use 'include?' so the values can be false value = request.options[param] elsif request.options.include?(param.to_s) value = request.options[param.to_s] end next if value.nil? value = true if value == "true" value = false if value == "false" value = Integer(value) if value.is_a?(String) and value =~ /^\d+$/ send(param.to_s + "=", value) end end private # Pull the recursion logic into one place. It's moderately hairy, and this # allows us to keep the hairiness apart from what we do with the files. def perform_recursion # Start out with just our base directory. current_dirs = [@path] next_dirs = [] depth = 1 result = [] return result unless recurse?(depth) while dir_path = current_dirs.shift or ((depth += 1) and recurse?(depth) and current_dirs = next_dirs and next_dirs = [] and dir_path = current_dirs.shift) next unless stat = stat(dir_path) next unless stat.directory? Dir.entries(dir_path).each do |file_path| next if [".", ".."].include?(file_path) # Note that this also causes matching directories not # to be recursed into. next if ignore?(file_path) # Add it to our list of files to return result << File.join(dir_path, file_path) # And to our list of files/directories to iterate over. next_dirs << File.join(dir_path, file_path) end end result end public # Stat a given file, using the links-appropriate method. def stat(path) @stat_method ||= self.links == :manage ? :lstat : :stat begin return File.send(@stat_method, path) rescue # If this happens, it is almost surely because we're # trying to manage a link to a file that does not exist. return nil end end end 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 @@ -1,83 +1,83 @@ require 'puppet/node/facts' require 'puppet/indirector/code' class Puppet::Node::Facts::Facter < Puppet::Indirector::Code desc "Retrieve facts from Facter. This provides a somewhat abstract interface between Puppet and Facter. It's only `somewhat` abstract because it always returns the local host's facts, regardless of what you attempt to find." 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 end def self.load_facts_in_dir(dir) return unless FileTest.directory?(dir) Dir.chdir(dir) do Dir.glob("*.rb").each do |file| fqfile = ::File.join(dir, file) begin Puppet.info "Loading facts in #{::File.basename(file.sub(".rb",''))}" Timeout::timeout(self.timeout) do load file end rescue SystemExit,NoMemoryError raise rescue Exception => detail Puppet.warning "Could not load fact file #{fqfile}: #{detail}" end end end end def self.timeout timeout = Puppet[:configtimeout] case timeout when String if timeout =~ /^\d+$/ timeout = Integer(timeout) else raise ArgumentError, "Configuration timeout must be an integer" end when Integer # nothing else raise ArgumentError, "Configuration timeout must be an integer" end timeout end def initialize(*args) super self.class.load_fact_plugins end def destroy(facts) raise Puppet::DevError, "You cannot destroy facts in the code store; it is only used for getting facts from Facter" end # Look a host's facts up in Facter. def find(request) result = Puppet::Node::Facts.new(request.key, Facter.to_hash) result.add_local_facts result.stringify result.downcase_if_necessary result end def save(facts) raise Puppet::DevError, "You cannot save facts to the code store; it is only used for getting facts from Facter" end end diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb index dc631979e..96fdc3c1e 100644 --- a/lib/puppet/node/environment.rb +++ b/lib/puppet/node/environment.rb @@ -1,178 +1,179 @@ require 'puppet/util/cacher' require 'monitor' # Just define it, so this class has fewer load dependencies. class Puppet::Node end # Model the environment that a node can operate in. This class just # provides a simple wrapper for the functionality around environments. class Puppet::Node::Environment module Helper def environment Puppet::Node::Environment.new(@environment) end def environment=(env) if env.is_a?(String) or env.is_a?(Symbol) @environment = env else @environment = env.name end end end include Puppet::Util::Cacher @seen = {} # Return an existing environment instance, or create a new one. def self.new(name = nil) return name if name.is_a?(self) name ||= Puppet.settings.value(:environment) raise ArgumentError, "Environment name must be specified" unless name symbol = name.to_sym return @seen[symbol] if @seen[symbol] obj = self.allocate obj.send :initialize, symbol @seen[symbol] = obj end def self.current Thread.current[:environment] || root end def self.current=(env) Thread.current[:environment] = new(env) end def self.root @root end # This is only used for testing. def self.clear @seen.clear end attr_reader :name # Return an environment-specific setting. def [](param) Puppet.settings.value(param, self.name) end def initialize(name) @name = name extend MonitorMixin end def known_resource_types # This makes use of short circuit evaluation to get the right thread-safe # per environment semantics with an efficient most common cases; we almost # always just return our thread's known-resource types. Only at the start # of a compilation (after our thread var has been set to nil) or when the # environment has changed do we delve deeper. Thread.current[:known_resource_types] = nil if (krt = Thread.current[:known_resource_types]) && krt.environment != self Thread.current[:known_resource_types] ||= synchronize { if @known_resource_types.nil? or @known_resource_types.require_reparse? @known_resource_types = Puppet::Resource::TypeCollection.new(self) @known_resource_types.import_ast(perform_initial_import, '') end @known_resource_types } end def module(name) mod = Puppet::Module.new(name, self) return nil unless mod.exist? mod end # Cache the modulepath, so that we aren't searching through # all known directories all the time. cached_attr(:modulepath, :ttl => Puppet[:filetimeout]) do dirs = self[:modulepath].split(File::PATH_SEPARATOR) dirs = ENV["PUPPETLIB"].split(File::PATH_SEPARATOR) + dirs if ENV["PUPPETLIB"] validate_dirs(dirs) end # Return all modules from this environment. # Cache the list, because it can be expensive to create. cached_attr(:modules, :ttl => Puppet[:filetimeout]) do module_names = modulepath.collect { |path| Dir.entries(path) }.flatten.uniq module_names.collect do |path| begin Puppet::Module.new(path, self) rescue Puppet::Module::Error => e nil end 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 def to_sym to_s.to_sym end # The only thing we care about when serializing an environment is its # identity; everything else is ephemeral and should not be stored or # transmitted. def to_zaml(z) self.to_s.to_zaml(z) end def validate_dirs(dirs) + dir_regex = Puppet.features.microsoft_windows? ? /^[A-Za-z]:#{File::SEPARATOR}/ : /^#{File::SEPARATOR}/ dirs.collect do |dir| - if dir !~ /^#{File::SEPARATOR}/ + if dir !~ dir_regex 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 private def perform_initial_import return empty_parse_result if Puppet.settings[:ignoreimport] parser = Puppet::Parser::Parser.new(self) if code = Puppet.settings.uninterpolated_value(:code, name.to_s) and code != "" parser.string = code else file = Puppet.settings.value(:manifest, name.to_s) parser.file = file end parser.parse rescue => detail known_resource_types.parse_failed = true msg = "Could not parse for environment #{self}: #{detail}" error = Puppet::Error.new(msg) error.set_backtrace(detail.backtrace) raise error end def empty_parse_result # Return an empty toplevel hostclass to use as the result of # perform_initial_import when no file was actually loaded. return Puppet::Parser::AST::Hostclass.new('') end @root = new(:'*root*') end 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 @@ -1,173 +1,174 @@ require 'puppet/node/environment' class Puppet::Parser::TypeLoader include Puppet::Node::Environment::Helper # Helper class that makes sure we don't try to import the same file # more than once from either the same thread or different threads. class Helper include MonitorMixin def initialize super # These hashes are indexed by filename @state = {} # :doing or :done @thread = {} # if :doing, thread that's doing the parsing @cond_var = {} # if :doing, condition var that will be signaled when done. end # Execute the supplied block exactly once per file, no matter how # many threads have asked for it to run. If another thread is # already executing it, wait for it to finish. If this thread is # already executing it, return immediately without executing the # block. # # Note: the reason for returning immediately if this thread is # already executing the block is to handle the case of a circular # import--when this happens, we attempt to recursively re-parse a # file that we are already in the process of parsing. To prevent # an infinite regress we need to simply do nothing when the # recursive import is attempted. def do_once(file) need_to_execute = synchronize do case @state[file] when :doing if @thread[file] != Thread.current @cond_var[file].wait end false when :done false else @state[file] = :doing @thread[file] = Thread.current @cond_var[file] = new_cond true end end if need_to_execute begin yield ensure synchronize do @state[file] = :done @thread.delete(file) @cond_var.delete(file).broadcast end end end end end # Import our files. def import(file, current_file = nil) return if Puppet[:ignoreimport] # use a path relative to the file doing the importing if current_file dir = current_file.sub(%r{[^/]+$},'').sub(/\/$/, '') else dir = "." end if dir == "" dir = "." end pat = file modname, files = Puppet::Parser::Files.find_manifests(pat, :cwd => dir, :environment => environment) if files.size == 0 raise Puppet::ImportError.new("No file(s) found for import of '#{pat}'") end 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 loaded_asts << parse_file(file) end end loaded_asts.inject([]) do |loaded_types, ast| loaded_types + known_resource_types.import_ast(ast, modname) end end def import_all require 'find' module_names = [] # Collect the list of all known modules environment.modulepath.each do |path| Dir.chdir(path) do Dir.glob("*").each do |dir| next unless FileTest.directory?(dir) module_names << dir end end end module_names.uniq! # And then load all files from each module, but (relying on system # behavior) only load files from the first module of a given name. E.g., # given first/foo and second/foo, only files from first/foo will be loaded. module_names.each do |name| mod = Puppet::Module.new(name, environment) Find.find(File.join(mod.path, "manifests")) do |path| if path =~ /\.pp$/ or path =~ /\.rb$/ import(path) end end end end def known_resource_types environment.known_resource_types end def initialize(env) self.environment = env @loading_helper = Helper.new end # Try to load the object with the given fully qualified name. def try_load_fqname(type, fqname) return nil if fqname == "" # special-case main. name2files(fqname).each do |filename| begin imported_types = import(filename) if result = imported_types.find { |t| t.type == type and t.name == fqname } Puppet.debug "Automatically imported #{fqname} from #{filename} into #{environment}" return result end rescue Puppet::ImportError => detail # We couldn't load the item # I'm not convienced we should just drop these errors, but this # preserves existing behaviours. end end # Nothing found. return nil end def parse_file(file) Puppet.debug("importing '#{file}' in environment #{environment}") parser = Puppet::Parser::Parser.new(environment) parser.file = file return parser.parse end private # Return a list of all file basenames that should be tried in order # to load the object with the given fully qualified name. def name2files(fqname) result = [] ary = fqname.split("::") while ary.length > 0 result << ary.join(File::SEPARATOR) ary.pop end return result end end diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb index 72e9a9495..8ab12ca2f 100644 --- a/lib/puppet/type/file.rb +++ b/lib/puppet/type/file.rb @@ -1,800 +1,800 @@ require 'digest/md5' require 'cgi' require 'etc' require 'uri' require 'fileutils' require 'puppet/network/handler' require 'puppet/util/diff' require 'puppet/util/checksums' require 'puppet/network/client' require 'puppet/util/backups' Puppet::Type.newtype(:file) do include Puppet::Util::MethodHelper include Puppet::Util::Checksums include Puppet::Util::Backups @doc = "Manages local files, including setting ownership and permissions, creation of both files and directories, and retrieving entire files from remote servers. As Puppet matures, it expected that the `file` resource will be used less and less to manage content, and instead native resources will be used to do so. If you find that you are often copying files in from a central 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 [ [ /^(.*?)\/*\Z/m, [ [ :path, lambda{|x| x} ] ] ] ] end newparam(:path) do desc "The path to the file to manage. Must be fully qualified." isnamevar 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 (Puppet.features.microsoft_windows? and (value =~ /^[A-Za-z]:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) fail Puppet::Error, "File paths must be fully qualified, not '#{value}'" end end # 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(/\/+/,'/')) { :index => Puppet::FileCollection.collection.index(path), :name => name } end # and the reverse unmunge do |value| basedir = Puppet::FileCollection.collection.path(value[:index]) # a lone slash as :name indicates a root dir on windows if value[:name] == '/' basedir else ::File.join( basedir, value[:name] ) end end end newparam(:backup) do desc "Whether files should be backed up before being replaced. The preferred method of backing files up is via a `filebucket`, which stores files by their MD5 sums and allows easy retrieval without littering directories with backups. You can specify a local filebucket or a network-accessible server-based filebucket by setting `backup => bucket-name`. Alternatively, if you specify any value that begins with a `.` (e.g., `.puppet-bak`), then Puppet will use copy the file in the same directory with that value as the extension of the backup. Setting `backup => false` disables all backups of the file in question. Puppet automatically creates a local filebucket named `puppet` and defaults to backing up there. To use a server-based filebucket, you must specify one in your configuration filebucket { main: server => puppet } The `puppet master` daemon creates a filebucket by default, so you can usually back up to your main server with this configuration. Once you've described the bucket in your configuration, you can use it in any file file { \"/my/file\": source => \"/path/in/nfs/or/something\", backup => main } This will back the file up to the central server. At this point, the benefits of using a filebucket are that you do not have backup files lying around on each of your machines, a given version of a file is only backed up once, and you can restore any given file manually, no matter how old. Eventually, transactional support will be able to automatically restore filebucketed files. " defaultto "puppet" munge do |value| # I don't really know how this is happening. value = value.shift if value.is_a?(Array) case value when false, "false", :false false when true, "true", ".puppet-bak", :true ".puppet-bak" when String value else self.fail "Invalid backup type #{value.inspect}" end end end newparam(:recurse) do desc "Whether and how deeply to do recursive management. Options are: * `inf,true` --- Regular style recursion on both remote and local directory structure. * `remote` --- Descends recursively into the remote directory but not the local directory. Allows copying of a few files into a directory containing many unmanaged files without scanning all the local files. * `false` --- Default of no recursion. * `[0-9]+` --- Same as true, but limit recursion. Warning: this syntax has been deprecated in favor of the `recurselimit` attribute. " newvalues(:true, :false, :inf, :remote, /^[0-9]+$/) # Replace the validation so that we allow numbers in # addition to string representations of them. validate { |arg| } munge do |value| newval = super(value) case newval when :true, :inf; true when :false; false when :remote; :remote when Integer, Fixnum, Bignum self.warning "Setting recursion depth with the recurse parameter is now deprecated, please use recurselimit" # recurse == 0 means no recursion return false if value == 0 resource[:recurselimit] = value true when /^\d+$/ self.warning "Setting recursion depth with the recurse parameter is now deprecated, please use recurselimit" value = Integer(value) # recurse == 0 means no recursion return false if value == 0 resource[:recurselimit] = value true else self.fail "Invalid recurse value #{value.inspect}" end end end newparam(:recurselimit) do desc "How deeply to do recursive management." newvalues(/^[0-9]+$/) munge do |value| newval = super(value) case newval when Integer, Fixnum, Bignum; value when /^\d+$/; Integer(value) else self.fail "Invalid recurselimit value #{value.inspect}" end end end newparam(:replace, :boolean => true) do desc "Whether or not to replace a file that is sourced but exists. This is useful for using file sources purely for initialization." newvalues(:true, :false) aliasvalue(:yes, :true) aliasvalue(:no, :false) defaultto :true end newparam(:force, :boolean => true) do desc "Force the file operation. Currently only used when replacing directories with links." newvalues(:true, :false) defaultto false end newparam(:ignore) do desc "A parameter which omits action on files matching specified patterns during recursion. Uses Ruby's builtin globbing engine, so shell metacharacters are fully supported, e.g. `[a-z]*`. Matches that would descend into the directory structure are ignored, e.g., `*/*`." validate do |value| unless value.is_a?(Array) or value.is_a?(String) or value == false self.devfail "Ignore must be a string or an Array" end end end newparam(:links) do desc "How to handle links during file actions. During file copying, `follow` will copy the target file instead of the link, `manage` will copy the link itself, and `ignore` will just pass it by. When not copying, `manage` and `ignore` behave equivalently (because you cannot really ignore links entirely during local recursion), and `follow` will manage the file to which the link points." newvalues(:follow, :manage) defaultto :manage end newparam(:purge, :boolean => true) do desc "Whether unmanaged files should be purged. If you have a filebucket configured the purged files will be uploaded, but if you do not, this will destroy data. Only use this option for generated files unless you really know what you are doing. This option only makes sense when recursively managing directories. Note that when using `purge` with `source`, Puppet will purge any files that are not on the remote system." defaultto :false newvalues(:true, :false) end newparam(:sourceselect) do desc "Whether to copy all valid sources, or just the first one. This parameter is only used in recursive copies; by default, the first valid source is the only one used as a recursive source, but if this parameter is set to `all`, then all valid sources will have all of their contents copied to the local host, and for sources that have the same file, the source earlier in the list will be used." defaultto :first newvalues(:first, :all) end # Autorequire the nearest ancestor directory found in the catalog. autorequire(:file) do basedir = ::File.dirname(self[:path]) if basedir != self[:path] parents = [] until basedir == parents.last parents << basedir basedir = ::File.dirname(basedir) end # The filename of the first ancestor found, or nil parents.find { |dir| catalog.resource(:file, dir) } else nil end end # Autorequire the owner and group of the file. {:user => :owner, :group => :group}.each do |type, property| autorequire(type) do if @parameters.include?(property) # The user/group property automatically converts to IDs next unless should = @parameters[property].shouldorig val = should[0] if val.is_a?(Integer) or val =~ /^\d+$/ nil else val end end end end CREATORS = [:content, :source, :target] SOURCE_ONLY_CHECKSUMS = [:none, :ctime, :mtime] validate do creator_count = 0 CREATORS.each do |param| creator_count += 1 if self.should(param) end creator_count += 1 if @parameters.include?(:source) self.fail "You cannot specify more than one of #{CREATORS.collect { |p| p.to_s}.join(", ")}" if creator_count > 1 self.fail "You cannot specify a remote recursion without a source" if !self[:source] and self[:recurse] == :remote self.fail "You cannot specify source when using checksum 'none'" if self[:checksum] == :none && !self[:source].nil? SOURCE_ONLY_CHECKSUMS.each do |checksum_type| self.fail "You cannot specify content when using checksum '#{checksum_type}'" if self[:checksum] == checksum_type && !self[:content].nil? end self.warning "Possible error: recurselimit is set but not recurse, no recursion will happen" if !self[:recurse] and self[:recurselimit] end def self.[](path) return nil unless path super(path.gsub(/\/+/, '/').sub(/\/$/, '')) end def self.instances(base = '/') return self.new(:name => base, :recurse => true, :recurselimit => 1, :audit => :all).recurse_local.values end # Determine the user to write files as. def asuser if self.should(:owner) and ! self.should(:owner).is_a?(Symbol) writeable = Puppet::Util::SUIDManager.asuser(self.should(:owner)) { FileTest.writable?(::File.dirname(self[:path])) } # If the parent directory is writeable, then we execute # as the user in question. Otherwise we'll rely on # the 'owner' property to do things. asuser = self.should(:owner) if writeable end asuser end def bucket return @bucket if @bucket backup = self[:backup] return nil unless backup return nil if backup =~ /^\./ unless catalog or backup == "puppet" fail "Can not find filebucket for backups without a catalog" end unless catalog and filebucket = catalog.resource(:filebucket, backup) or backup == "puppet" fail "Could not find filebucket #{backup} specified in backup" end return default_bucket unless filebucket @bucket = filebucket.bucket @bucket end def default_bucket Puppet::Type.type(:filebucket).mkdefaultbucket.bucket end # Does the file currently exist? Just checks for whether # we have a stat def exist? stat ? true : false end # We have to do some extra finishing, to retrieve our bucket if # there is one. def finish # Look up our bucket, if there is one bucket super end # Create any children via recursion or whatever. def eval_generate return [] unless self.recurse? recurse #recurse.reject do |resource| # catalog.resource(:file, resource[:path]) #end.each do |child| # catalog.add_resource child # catalog.relationship_graph.add_edge self, child #end end def flush # We want to make sure we retrieve metadata anew on each transaction. @parameters.each do |name, param| param.flush if param.respond_to?(:flush) end @stat = nil end def initialize(hash) # Used for caching clients @clients = {} super # If they've specified a source, we get our 'should' values # from it. unless self[:ensure] if self[:target] self[:ensure] = :symlink elsif self[:content] self[:ensure] = :file end end @stat = nil end # Configure discovered resources to be purged. def mark_children_for_purging(children) children.each do |name, child| next if child[:source] child[:ensure] = :absent end end # Create a new file or directory object as a child to the current # object. def newchild(path) full_path = ::File.join(self[:path], path) # Add some new values to our original arguments -- these are the ones # set at initialization. We specifically want to exclude any param # values set by the :source property or any default values. # LAK:NOTE This is kind of silly, because the whole point here is that # the values set at initialization should live as long as the resource # but values set by default or by :source should only live for the transaction # or so. Unfortunately, we don't have a straightforward way to manage # the different lifetimes of this data, so we kludge it like this. # The right-side hash wins in the merge. options = @original_parameters.merge(:path => full_path).reject { |param, value| value.nil? } # These should never be passed to our children. [:parent, :ensure, :recurse, :recurselimit, :target, :alias, :source].each do |param| options.delete(param) if options.include?(param) end self.class.new(options) end # Files handle paths specially, because they just lengthen their # path names, rather than including the full parent's title each # time. def pathbuilder # We specifically need to call the method here, so it looks # up our parent in the catalog graph. if parent = parent() # We only need to behave specially when our parent is also # a file if parent.is_a?(self.class) # Remove the parent file name list = parent.pathbuilder list.pop # remove the parent's path info return list << self.ref else return super end else return [self.ref] end end # Should we be purging? def purge? @parameters.include?(:purge) and (self[:purge] == :true or self[:purge] == "true") end # Recursively generate a list of file resources, which will # be used to copy remote files, manage local files, and/or make links # to map to another directory. def recurse children = (self[:recurse] == :remote) ? {} : recurse_local if self[:target] recurse_link(children) elsif self[:source] recurse_remote(children) end # If we're purging resources, then delete any resource that isn't on the # remote system. mark_children_for_purging(children) if self.purge? result = children.values.sort { |a, b| a[:path] <=> b[:path] } remove_less_specific_files(result) end # This is to fix bug #2296, where two files recurse over the same # set of files. It's a rare case, and when it does happen you're # not likely to have many actual conflicts, which is good, because # this is a pretty inefficient implementation. def remove_less_specific_files(files) mypath = self[:path].split(::File::Separator) other_paths = catalog.vertices. select { |r| r.is_a?(self.class) and r[:path] != self[:path] }. collect { |r| r[:path].split(::File::Separator) }. select { |p| p[0,mypath.length] == mypath } return files if other_paths.empty? files.reject { |file| path = file[:path].split(::File::Separator) other_paths.any? { |p| path[0,p.length] == p } } end # A simple method for determining whether we should be recursing. def recurse? self[:recurse] == true or self[:recurse] == :remote end # Recurse the target of the link. def recurse_link(children) perform_recursion(self[:target]).each do |meta| if meta.relative_path == "." self[:ensure] = :directory next end children[meta.relative_path] ||= newchild(meta.relative_path) if meta.ftype == "directory" children[meta.relative_path][:ensure] = :directory else children[meta.relative_path][:ensure] = :link children[meta.relative_path][:target] = meta.full_path end end children end # Recurse the file itself, returning a Metadata instance for every found file. def recurse_local result = perform_recursion(self[:path]) return {} unless result result.inject({}) do |hash, meta| next hash if meta.relative_path == "." hash[meta.relative_path] = newchild(meta.relative_path) hash end end # Recurse against our remote file. def recurse_remote(children) sourceselect = self[:sourceselect] total = self[:source].collect do |source| next unless result = perform_recursion(source) return if top = result.find { |r| r.relative_path == "." } and top.ftype != "directory" result.each { |data| data.source = "#{source}/#{data.relative_path}" } break result if result and ! result.empty? and sourceselect == :first result end.flatten # This only happens if we have sourceselect == :all unless sourceselect == :first found = [] total.reject! do |data| result = found.include?(data.relative_path) found << data.relative_path unless found.include?(data.relative_path) result end end total.each do |meta| if meta.relative_path == "." parameter(:source).metadata = meta next end children[meta.relative_path] ||= newchild(meta.relative_path) children[meta.relative_path][:source] = meta.source children[meta.relative_path][:checksum] = :md5 if meta.ftype == "file" children[meta.relative_path].parameter(:source).metadata = meta end children end def perform_recursion(path) Puppet::FileServing::Metadata.indirection.search( path, :links => self[:links], :recurse => (self[:recurse] == :remote ? true : self[:recurse]), :recurselimit => self[:recurselimit], :ignore => self[:ignore], :checksum_type => (self[:source] || self[:content]) ? self[:checksum] : :none ) end # Remove any existing data. This is only used when dealing with # links or directories. def remove_existing(should) return unless s = stat self.fail "Could not back up; will not replace" unless perform_backup unless should.to_s == "link" return if s.ftype.to_s == should.to_s end case s.ftype when "directory" if self[:force] == :true debug "Removing existing directory for replacement with #{should}" FileUtils.rmtree(self[:path]) else notice "Not removing directory; use 'force' to override" end when "link", "file" debug "Removing existing #{s.ftype} for replacement with #{should}" ::File.unlink(self[:path]) else self.fail "Could not back up files of type #{s.ftype}" end expire end def retrieve if source = parameter(:source) source.copy_source_values end super end # Set the checksum, from another property. There are multiple # properties that modify the contents of a file, and they need the # ability to make sure that the checksum value is in sync. def setchecksum(sum = nil) if @parameters.include? :checksum if sum @parameters[:checksum].checksum = sum else # If they didn't pass in a sum, then tell checksum to # figure it out. currentvalue = @parameters[:checksum].retrieve @parameters[:checksum].checksum = currentvalue end end end # Should this thing be a normal file? This is a relatively complex # way of determining whether we're trying to create a normal file, # and it's here so that the logic isn't visible in the content property. def should_be_file? return true if self[:ensure] == :file # I.e., it's set to something like "directory" return false if e = self[:ensure] and e != :present # The user doesn't really care, apparently if self[:ensure] == :present return true unless s = stat return(s.ftype == "file" ? true : false) end # If we've gotten here, then :ensure isn't set return true if self[:content] return true if stat and stat.ftype == "file" false end # Stat our file. Depending on the value of the 'links' attribute, we # 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 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 ::File.send(method, self[:path]) rescue Errno::ENOENT => error return nil rescue Errno::EACCES => error warning "Could not stat; permission denied" return nil end end # We have to hack this just a little bit, because otherwise we'll get # an error when the target and the contents are created as properties on # the far side. def to_trans(retrieve = true) obj = super obj.delete(:target) if obj[:target] == :notlink obj end # Write out the file. Requires the property name for logging. # Write will be done by the content property, along with checksum computation def write(property) remove_existing(:file) use_temporary_file = write_temporary_file? if use_temporary_file path = "#{self[:path]}.puppettmp_#{rand(10000)}" path = "#{self[:path]}.puppettmp_#{rand(10000)}" while ::File.exists?(path) or ::File.symlink?(path) else path = self[:path] end mode = self.should(:mode) # might be nil umask = mode ? 000 : 022 mode_int = mode ? mode.to_i(8) : nil content_checksum = Puppet::Util.withumask(umask) { ::File.open(path, 'w', mode_int ) { |f| write_content(f) } } # And put our new file in place if use_temporary_file # This is only not true when our file is empty. begin fail_if_checksum_is_wrong(path, content_checksum) if validate_checksum? ::File.rename(path, self[:path]) rescue => detail fail "Could not rename temporary file #{path} to #{self[:path]}: #{detail}" ensure # Make sure the created file gets removed ::File.unlink(path) if FileTest.exists?(path) end end # make sure all of the modes are actually correct property_fix end private # Should we validate the checksum of the file we're writing? def validate_checksum? self[:checksum] !~ /time/ end # Make sure the file we wrote out is what we think it is. def fail_if_checksum_is_wrong(path, content_checksum) newsum = parameter(:checksum).sum_file(path) return if [:absent, nil, content_checksum].include?(newsum) self.fail "File written to disk did not match checksum; discarding changes (#{content_checksum} vs #{newsum})" end # write the current content. Note that if there is no content property # simply opening the file with 'w' as done in write is enough to truncate # or write an empty length file. def write_content(file) (content = property(:content)) && content.write(file) end private def write_temporary_file? # unfortunately we don't know the source file size before fetching it # so let's assume the file won't be empty (c = property(:content) and c.length) || (s = @parameters[:source] and 1) end # There are some cases where all of the work does not get done on # file creation/modification, so we have to do some extra checking. def property_fix properties.each do |thing| next unless [:mode, :owner, :group, :seluser, :selrole, :seltype, :selrange].include?(thing.name) # Make sure we get a new stat objct expire currentvalue = thing.retrieve thing.sync unless thing.safe_insync?(currentvalue) end end end # We put all of the properties in separate files, because there are so many # of them. The order these are loaded is important, because it determines # the order they are in the property lit. require 'puppet/type/file/checksum' require 'puppet/type/file/content' # can create the file require 'puppet/type/file/source' # can create the file require 'puppet/type/file/target' # creates a different type of file require 'puppet/type/file/ensure' # can create the file require 'puppet/type/file/owner' require 'puppet/type/file/group' require 'puppet/type/file/mode' require 'puppet/type/file/type' require 'puppet/type/file/selcontext' # SELinux file context require 'puppet/type/file/ctime' require 'puppet/type/file/mtime' diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb index 9412976f0..47fd93a03 100755 --- a/spec/integration/application/doc_spec.rb +++ b/spec/integration/application/doc_spec.rb @@ -1,54 +1,54 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet_spec/files' describe Puppet::Application::Doc do include PuppetSpec::Files - it "should not generate an error when module dir overlaps parent of site.pp (#4798)", :'fails_on_ruby_1.9.2' => true do + it "should not generate an error when module dir overlaps parent of site.pp (#4798)", :'fails_on_ruby_1.9.2' => true, :fails_on_windows => true do begin # Note: the directory structure below is more complex than it # needs to be, but it's representative of the directory structure # used in bug #4798. old_dir = Dir.getwd # Note: can't use chdir with a block because it will generate bogus warnings tmpdir = tmpfile('doc_spec') Dir.mkdir(tmpdir) Dir.chdir(tmpdir) site_file = 'site.pp' File.open(site_file, 'w') do |f| f.puts '# A comment' end modules_dir = 'modules' Dir.mkdir(modules_dir) rt_dir = File.join(modules_dir, 'rt') Dir.mkdir(rt_dir) manifests_dir = File.join(rt_dir, 'manifests') Dir.mkdir(manifests_dir) rt_file = File.join(manifests_dir, 'rt.pp') File.open(rt_file, 'w') do |f| f.puts '# A class' f.puts 'class foo { }' f.puts '# A definition' f.puts 'define bar { }' end puppet = Puppet::Application[:doc] Puppet[:modulepath] = modules_dir Puppet[:manifest] = site_file puppet.options[:mode] = :rdoc expect { puppet.run_command }.to exit_with 0 File.should be_exist('doc') ensure Dir.chdir(old_dir) end end it "should respect the -o option" do puppetdoc = Puppet::Application[:doc] puppetdoc.command_line.stubs(:args).returns(['foo', '-o', 'bar']) puppetdoc.parse_options puppetdoc.options[:outputdir].should == 'bar' end end diff --git a/spec/integration/file_serving/terminus_helper_spec.rb b/spec/integration/file_serving/terminus_helper_spec.rb index 7500b1fc0..99fee9ce5 100755 --- a/spec/integration/file_serving/terminus_helper_spec.rb +++ b/spec/integration/file_serving/terminus_helper_spec.rb @@ -1,21 +1,21 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/file_serving/terminus_helper' class TerminusHelperIntegrationTester include Puppet::FileServing::TerminusHelper def model Puppet::FileServing::Metadata end end -describe Puppet::FileServing::TerminusHelper do +describe Puppet::FileServing::TerminusHelper, :fails_on_windows => true do it "should be able to recurse on a single file" do @path = Tempfile.new("fileset_integration") request = Puppet::Indirector::Request.new(:metadata, :find, @path.path, :recurse => true) tester = TerminusHelperIntegrationTester.new lambda { tester.path2instances(request, @path.path) }.should_not raise_error end end diff --git a/spec/integration/indirector/direct_file_server_spec.rb b/spec/integration/indirector/direct_file_server_spec.rb index e53b48d69..68ed00740 100755 --- a/spec/integration/indirector/direct_file_server_spec.rb +++ b/spec/integration/indirector/direct_file_server_spec.rb @@ -1,73 +1,75 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-19. # Copyright (c) 2007. All rights reserved. require 'spec_helper' require 'puppet/indirector/file_content/file' -describe Puppet::Indirector::DirectFileServer, " when interacting with the filesystem and the model" do +describe Puppet::Indirector::DirectFileServer, " when interacting with the filesystem and the model", :fails_on_windows => true do + include PuppetSpec::Files + before do # We just test a subclass, since it's close enough. @terminus = Puppet::Indirector::FileContent::File.new - @filepath = "/path/to/my/file" + @filepath = make_absolute("/path/to/my/file") end it "should return an instance of the model" do FileTest.expects(:exists?).with(@filepath).returns(true) @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}")).should be_instance_of(Puppet::FileServing::Content) end it "should return an instance capable of returning its content" do FileTest.expects(:exists?).with(@filepath).returns(true) File.stubs(:lstat).with(@filepath).returns(stub("stat", :ftype => "file")) File.expects(:read).with(@filepath).returns("my content") instance = @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}")) instance.content.should == "my content" end end -describe Puppet::Indirector::DirectFileServer, " when interacting with FileServing::Fileset and the model" do +describe Puppet::Indirector::DirectFileServer, " when interacting with FileServing::Fileset and the model", :fails_on_windows => true do before do @terminus = Puppet::Indirector::FileContent::File.new @path = Tempfile.new("direct_file_server_testing") path = @path.path @path.close! @path = path Dir.mkdir(@path) File.open(File.join(@path, "one"), "w") { |f| f.print "one content" } File.open(File.join(@path, "two"), "w") { |f| f.print "two content" } @request = @terminus.indirection.request(:search, "file:///#{@path}", :recurse => true) end after do system("rm -rf #{@path}") end it "should return an instance for every file in the fileset" do result = @terminus.search(@request) result.should be_instance_of(Array) result.length.should == 3 result.each { |r| r.should be_instance_of(Puppet::FileServing::Content) } end it "should return instances capable of returning their content" do @terminus.search(@request).each do |instance| case instance.full_path when /one/; instance.content.should == "one content" when /two/; instance.content.should == "two content" when @path else raise "No valid key for #{instance.path.inspect}" end end end end diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb index 88d2345d8..2a8c134a4 100755 --- a/spec/integration/indirector/file_content/file_server_spec.rb +++ b/spec/integration/indirector/file_content/file_server_spec.rb @@ -1,94 +1,94 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. require 'spec_helper' require 'puppet/indirector/file_content/file_server' require 'shared_behaviours/file_server_terminus' require 'puppet_spec/files' -describe Puppet::Indirector::FileContent::FileServer, " when finding files" do +describe Puppet::Indirector::FileContent::FileServer, " when finding files", :fails_on_windows => true do it_should_behave_like "Puppet::Indirector::FileServerTerminus" include PuppetSpec::Files before do @terminus = Puppet::Indirector::FileContent::FileServer.new @test_class = Puppet::FileServing::Content end it "should find plugin file content in the environment specified in the request" do path = tmpfile("file_content_with_env") Dir.mkdir(path) modpath = File.join(path, "mod") FileUtils.mkdir_p(File.join(modpath, "lib")) file = File.join(modpath, "lib", "file.rb") File.open(file, "w") { |f| f.puts "1" } Puppet.settings[:modulepath] = "/no/such/file" env = Puppet::Node::Environment.new("foo") env.stubs(:modulepath).returns [path] result = Puppet::FileServing::Content.indirection.search("plugins", :environment => "foo", :recurse => true) result.should_not be_nil result.length.should == 2 result[1].should be_instance_of(Puppet::FileServing::Content) result[1].content.should == "1\n" end it "should find file content in modules" do path = tmpfile("file_content") Dir.mkdir(path) modpath = File.join(path, "mymod") FileUtils.mkdir_p(File.join(modpath, "files")) file = File.join(modpath, "files", "myfile") File.open(file, "w") { |f| f.puts "1" } Puppet.settings[:modulepath] = path result = Puppet::FileServing::Content.indirection.find("modules/mymod/myfile") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\n" end it "should find file content in files when node name expansions are used" do Puppet::Util::Cacher.expire FileTest.stubs(:exists?).returns true FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true) @path = tmpfile("file_server_testing") Dir.mkdir(@path) subdir = File.join(@path, "mynode") Dir.mkdir(subdir) File.open(File.join(subdir, "myfile"), "w") { |f| f.puts "1" } # Use a real mount, so the integration is a bit deeper. @mount1 = Puppet::FileServing::Configuration::Mount::File.new("one") @mount1.stubs(:allowed?).returns true @mount1.path = File.join(@path, "%h") @parser = stub 'parser', :changed? => false @parser.stubs(:parse).returns("one" => @mount1) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) path = File.join(@path, "myfile") result = Puppet::FileServing::Content.indirection.find("one/myfile", :environment => "foo", :node => "mynode") result.should_not be_nil result.should be_instance_of(Puppet::FileServing::Content) result.content.should == "1\n" end end diff --git a/spec/integration/indirector/file_metadata/file_server_spec.rb b/spec/integration/indirector/file_metadata/file_server_spec.rb index 9e84134a6..5259837fc 100755 --- a/spec/integration/indirector/file_metadata/file_server_spec.rb +++ b/spec/integration/indirector/file_metadata/file_server_spec.rb @@ -1,18 +1,18 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2007-10-18. # Copyright (c) 2007. All rights reserved. require 'spec_helper' require 'puppet/indirector/file_metadata/file_server' require 'shared_behaviours/file_server_terminus' -describe Puppet::Indirector::FileMetadata::FileServer, " when finding files" do +describe Puppet::Indirector::FileMetadata::FileServer, " when finding files", :fails_on_windows => true do it_should_behave_like "Puppet::Indirector::FileServerTerminus" before do @terminus = Puppet::Indirector::FileMetadata::FileServer.new @test_class = Puppet::FileServing::Metadata end end diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb index 9f6aae907..51611f888 100755 --- a/spec/integration/parser/compiler_spec.rb +++ b/spec/integration/parser/compiler_spec.rb @@ -1,133 +1,133 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Parser::Compiler do before :each do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") end after do Puppet.settings.clear end - it "should be able to determine the configuration version from a local version control repository" do + it "should be able to determine the configuration version from a local version control repository", :fails_on_windows => true do # This should always work, because we should always be # in the puppet repo when we run this. version = %x{git rev-parse HEAD}.chomp Puppet.settings[:config_version] = 'git rev-parse HEAD' @parser = Puppet::Parser::Parser.new "development" @compiler = Puppet::Parser::Compiler.new(@node) @compiler.catalog.version.should == version end it "should not create duplicate resources when a class is referenced both directly and indirectly by the node classifier (4792)" do Puppet[:code] = <<-PP class foo { notify { foo_notify: } include bar } class bar { notify { bar_notify: } } PP @node.stubs(:classes).returns(['foo', 'bar']) catalog = Puppet::Parser::Compiler.compile(@node) catalog.resource("Notify[foo_notify]").should_not be_nil catalog.resource("Notify[bar_notify]").should_not be_nil end describe "when resolving class references" do it "should favor local scope, even if there's an included class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include baz include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end it "should favor local scope, even if there's an unincluded class in topscope" do Puppet[:code] = <<-PP class experiment { class baz { } notify {"x" : require => Class[Baz] } } class baz { } include experiment include experiment::baz PP catalog = Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) notify_resource = catalog.resource( "Notify[x]" ) notify_resource[:require].title.should == "Experiment::Baz" end end it "should recompute the version after input files are re-parsed" do Puppet[:code] = 'class foo { }' Time.stubs(:now).returns(1) node = Puppet::Node.new('mynode') Puppet::Parser::Compiler.compile(node).version.should == 1 Time.stubs(:now).returns(2) Puppet::Parser::Compiler.compile(node).version.should == 1 # no change because files didn't change Puppet::Resource::TypeCollection.any_instance.stubs(:stale?).returns(true).then.returns(false) # pretend change Puppet::Parser::Compiler.compile(node).version.should == 2 end ['class', 'define', 'node'].each do |thing| it "should not allow #{thing} inside evaluated conditional constructs" do Puppet[:code] = <<-PP if true { #{thing} foo { } notify { decoy: } } PP begin Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) raise "compilation should have raised Puppet::Error" rescue Puppet::Error => e e.message.should =~ /at line 2/ end end end it "should not allow classes inside unevaluated conditional constructs" do Puppet[:code] = <<-PP if false { class foo { } } PP lambda { Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) }.should raise_error(Puppet::Error) end end diff --git a/spec/integration/parser/ruby_manifest_spec.rb b/spec/integration/parser/ruby_manifest_spec.rb index 7f3bb71e9..b04baf5e0 100755 --- a/spec/integration/parser/ruby_manifest_spec.rb +++ b/spec/integration/parser/ruby_manifest_spec.rb @@ -1,127 +1,127 @@ #!/usr/bin/env rspec require 'spec_helper' require 'tempfile' require 'puppet_spec/files' -describe "Pure ruby manifests" do +describe "Pure ruby manifests", :fails_on_windows => true do include PuppetSpec::Files before do @node = Puppet::Node.new "testnode" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]' @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") @test_dir = tmpdir('ruby_manifest_test') end after do Puppet.settings.clear end def write_file(name, contents) path = File.join(@test_dir, name) File.open(path, "w") { |f| f.write(contents) } path end def compile(contents) Puppet[:code] = contents Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end end it "should allow classes" do write_file('foo.rb', ["hostclass 'one' do notify('one_notify') end", "hostclass 'two' do notify('two_notify') end"].join("\n")) catalog = compile("import 'foo'\ninclude one") catalog.resource("Notify[one_notify]").should_not be_nil catalog.resource("Notify[two_notify]").should be_nil end it "should allow defines" do write_file('foo.rb', 'define "bar", :arg do notify("bar_#{@name}_#{@arg}") end') catalog = compile("import 'foo'\nbar { instance: arg => 'xyz' }") catalog.resource("Notify[bar_instance_xyz]").should_not be_nil catalog.resource("Bar[instance]").should_not be_nil end it "should allow node declarations" do write_file('foo.rb', "node 'mynode' do notify('mynode') end") catalog = compile("import 'foo'") node_declaration = catalog.resource("Notify[mynode]") node_declaration.should_not be_nil node_declaration.title.should == 'mynode' end it "should allow access to the environment" do write_file('foo.rb', ["hostclass 'bar' do", " if environment.is_a? Puppet::Node::Environment", " notify('success')", " end", "end"].join("\n")) compile("import 'foo'\ninclude bar").resource("Notify[success]").should_not be_nil end it "should allow creation of resources of built-in types" do write_file('foo.rb', "hostclass 'bar' do file 'test_file', :owner => 'root', :mode => '644' end") catalog = compile("import 'foo'\ninclude bar") file = catalog.resource("File[test_file]") file.should be_a(Puppet::Resource) file.type.should == 'File' file.title.should == 'test_file' file.exported.should_not be file.virtual.should_not be file[:owner].should == 'root' file[:mode].should == '644' file[:stage].should be_nil # TODO: is this correct behavior? end it "should allow calling user-defined functions" do write_file('foo.rb', "hostclass 'bar' do user_func 'name', :arg => 'xyz' end") catalog = compile(['define user_func($arg) { notify {"n_$arg": } }', 'import "foo"', 'include bar'].join("\n")) catalog.resource("Notify[n_xyz]").should_not be_nil catalog.resource("User_func[name]").should_not be_nil end it "should be properly cached for multiple compiles" do # Note: we can't test this by calling compile() twice, because # that sets Puppet[:code], which clears out all cached # environments. Puppet[:filetimeout] = 1000 write_file('foo.rb', "hostclass 'bar' do notify('success') end") Puppet[:code] = "import 'foo'\ninclude bar" # Compile the catalog and check it catalog = Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end catalog.resource("Notify[success]").should_not be_nil # Secretly change the file to make it invalid. This change # shouldn't be noticed because the we've set a high # Puppet[:filetimeout]. write_file('foo.rb', "raise 'should not be executed'") # Compile the catalog a second time and make sure it's still ok. catalog = Dir.chdir(@test_dir) do Puppet::Parser::Compiler.compile(Puppet::Node.new("mynode")) end catalog.resource("Notify[success]").should_not be_nil end it "should be properly reloaded when stale" do Puppet[:filetimeout] = -1 # force stale check to happen all the time write_file('foo.rb', "hostclass 'bar' do notify('version1') end") catalog = compile("import 'foo'\ninclude bar") catalog.resource("Notify[version1]").should_not be_nil sleep 1 # so that timestamp will change forcing file reload write_file('foo.rb', "hostclass 'bar' do notify('version2') end") catalog = compile("import 'foo'\ninclude bar") catalog.resource("Notify[version1]").should be_nil catalog.resource("Notify[version2]").should_not be_nil end end diff --git a/spec/integration/provider/mount_spec.rb b/spec/integration/provider/mount_spec.rb index 4af0dca4a..eb8cc134a 100755 --- a/spec/integration/provider/mount_spec.rb +++ b/spec/integration/provider/mount_spec.rb @@ -1,151 +1,151 @@ require 'spec_helper' require 'puppet/file_bucket/dipper' -describe "mount provider (integration)" do +describe "mount provider (integration)", :fails_on_windows => true do include PuppetSpec::Files def create_fake_fstab(initially_contains_entry) File.open(@fake_fstab, 'w') do |f| if initially_contains_entry f.puts("/dev/disk1s1\t/Volumes/foo_disk\tmsdos\tlocal\t0\t0") end end end before :each do @fake_fstab = tmpfile('fstab') @current_options = "local" @current_device = "/dev/disk1s1" Puppet::Type.type(:mount).defaultprovider.stubs(:default_target).returns(@fake_fstab) Facter.stubs(:value).with(:operatingsystem).returns('Darwin') Puppet::Util::ExecutionStub.set do |command, options| case command[0] when %r{/s?bin/mount} if command.length == 1 if @mounted "#{@current_device} on /Volumes/foo_disk (msdos, #{@current_options})\n" else '' end else command.length.should == 4 command[1].should == '-o' command[3].should == '/Volumes/foo_disk' @mounted.should == false # verify that we don't try to call "mount" redundantly @current_options = command[2] @current_device = check_fstab(true) @mounted = true '' end when %r{/s?bin/umount} command.length.should == 2 command[1].should == '/Volumes/foo_disk' @mounted.should == true # "umount" doesn't work when device not mounted (see #6632) @mounted = false '' else fail "Unexpected command #{command.inspect} executed" end end end after :each do Puppet::Type::Mount::ProviderParsed.clear # Work around bug #6628 end def check_fstab(expected_to_be_present) # Verify that the fake fstab has the expected data in it fstab_contents = File.read(@fake_fstab).split("\n").reject { |x| x =~ /^#|^$/ } if expected_to_be_present fstab_contents.length().should == 1 device, rest_of_line = fstab_contents[0].split(/\t/,2) rest_of_line.should == "/Volumes/foo_disk\tmsdos\t#{@desired_options}\t0\t0" device else fstab_contents.length().should == 0 nil end end def run_in_catalog(settings) resource = Puppet::Type.type(:mount).new(settings.merge(:name => "/Volumes/foo_disk", :device => "/dev/disk1s1", :fstype => "msdos")) Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to the filebucket resource.expects(:err).never catalog = Puppet::Resource::Catalog.new catalog.host_config = false # Stop Puppet from doing a bunch of magic catalog.add_resource resource catalog.apply end [false, true].each do |initial_state| describe "When initially #{initial_state ? 'mounted' : 'unmounted'}" do before :each do @mounted = initial_state end [false, true].each do |initial_fstab_entry| describe "When there is #{initial_fstab_entry ? 'an' : 'no'} initial fstab entry" do before :each do create_fake_fstab(initial_fstab_entry) end [:defined, :present, :mounted, :unmounted, :absent].each do |ensure_setting| expected_final_state = case ensure_setting when :mounted true when :unmounted, :absent false when :defined, :present initial_state else fail "Unknown ensure_setting #{ensure_setting}" end expected_fstab_data = (ensure_setting != :absent) describe "When setting ensure => #{ensure_setting}" do ["local", "journaled"].each do |options_setting| describe "When setting options => #{options_setting}" do it "should leave the system in the #{expected_final_state ? 'mounted' : 'unmounted'} state, #{expected_fstab_data ? 'with' : 'without'} data in /etc/fstab" do @desired_options = options_setting run_in_catalog(:ensure=>ensure_setting, :options => options_setting) @mounted.should == expected_final_state if expected_fstab_data check_fstab(expected_fstab_data).should == "/dev/disk1s1" else check_fstab(expected_fstab_data).should == nil end if @mounted if ![:defined, :present].include?(ensure_setting) @current_options.should == @desired_options elsif initial_fstab_entry @current_options.should == @desired_options else @current_options.should == 'local' #Workaround for #6645 end end end end end end end end end end end describe "When the wrong device is mounted" do it "should remount the correct device" do pending "Due to bug 6309" @mounted = true @current_device = "/dev/disk2s2" create_fake_fstab(true) @desired_options = "local" run_in_catalog(:ensure=>:mounted, :options=>'local') @current_device.should=="/dev/disk1s1" @mounted.should==true @current_options.should=='local' check_fstab(true).should == "/dev/disk1s1" end end end diff --git a/spec/integration/provider/package_spec.rb b/spec/integration/provider/package_spec.rb index 5fecdf13c..701752371 100755 --- a/spec/integration/provider/package_spec.rb +++ b/spec/integration/provider/package_spec.rb @@ -1,23 +1,23 @@ #!/usr/bin/env rspec require 'spec_helper' describe "Package Provider", :'fails_on_ruby_1.9.2' => true do Puppet::Type.type(:package).providers.each do |name| provider = Puppet::Type.type(:package).provider(name) describe name, :if => provider.suitable? do it "should fail when asked to install an invalid package" do pending("This test hangs forever with recent versions of RubyGems") if provider.name == :gem pkg = Puppet::Type.newpackage :name => "nosuch#{provider.name}", :provider => provider.name lambda { pkg.provider.install }.should raise_error end - it "should be able to get a list of existing packages" do + it "should be able to get a list of existing packages", :fails_on_windows => true do provider.instances.each do |package| package.should be_instance_of(provider) package.properties[:provider].should == provider.name end end end end end diff --git a/spec/integration/provider/ssh_authorized_key_spec.rb b/spec/integration/provider/ssh_authorized_key_spec.rb old mode 100644 new mode 100755 index 902f9ad22..f7f61ab25 --- a/spec/integration/provider/ssh_authorized_key_spec.rb +++ b/spec/integration/provider/ssh_authorized_key_spec.rb @@ -1,207 +1,207 @@ #!/usr/bin/env ruby require 'spec_helper' require 'puppet/file_bucket/dipper' -describe "ssh_authorized_key provider (integration)" do +describe "ssh_authorized_key provider (integration)", :fails_on_windows => true do include PuppetSpec::Files before :each do @fake_userfile = tmpfile('authorized_keys.user') @fake_rootfile = tmpfile('authorized_keys.root') # few testkeys generated with ssh-keygen @sample_rsa_keys = [ 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQCi18JBZOq10X3w4f67nVhO0O3s5Y1vHH4UgMSM3ZnQwbC5hjGyYSi9UULOoQQoQynI/a0I9NL423/Xk/XJVIKCHcS8q6V2Wmjd+fLNelOjxxoW6mbIytEt9rDvwgq3Mof3/m21L3t2byvegR00a+ikKbmInPmKwjeWZpexCIsHzQ==', # 1024 bit 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLClyvi3CsJw5Id6khZs2/+s11qOH4Gdp6iDioDsrIp0m8kSiPr71VGyQYAfPzzvHemHS7Xg0NkG1Kc8u9tRqBQfTvz7ubq0AT/g01+4P2hQ/soFkuwlUG/HVnnaYb6N0Qp5SHWvD5vBE2nFFQVpP5GrSctPtHSjzJq/i+6LYhmQ==', # 1024 bit 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDLygAO6txXkh9FNV8xSsBkATeqLbHzS7sFjGI3gt0Dx6q3LjyKwbhQ1RLf28kd5G6VWiXmClU/RtiPdUz8nrGuun++2mrxzrXrvpR9dq1lygLQ2wn2cI35dN5bjRMtXy3decs6HUhFo9MoNwX250rUWfdCyNPhGIp6OOfmjdy+UeLGNxq9wDx6i4bT5tVVSqVRtsEfw9+ICXchzl85QudjneVVpP+thriPZXfXA5eaGwAo/dmoKOIhUwF96gpdLqzNtrGQuxPbV80PTbGv9ZtAtTictxaDz8muXO7he9pXmchUpxUKtMFjHkL0FAZ9tRPmv3RA30sEr2fZ8+LKvnE50w0' #2048 Bit ] @sample_dsa_keys = [ 'AAAAB3NzaC1kc3MAAACBAOPck2O8MIDSqxPSnvENt6tzRrKJ5oOhB6Nc6oEcWm+VEH1gvuxdiRqwoMgRwyEf1yUd+UAcLw3a6Jn+EtFyEBN/5WF+4Tt4xTxZ0Pfik2Wc5uqHbQ2dkmOoXiAOYPiD3JUQ1Xwm/J0CgetjitoLfzAGdCNhMqguqAuHcVJ78ZZbAAAAFQCIBKFYZ+I18I+dtgteirXh+VVEEwAAAIEAs1yvQ/wnLLrRCM660pF4kBiw3D6dJfMdCXWQpn0hZmkBQSIzZv4Wuk3giei5luxscDxNc+y3CTXtnyG4Kt1Yi2sOdvhRI3rX8tD+ejn8GHazM05l5VIo9uu4AQPIE32iV63IqgApSBbJ6vDJW91oDH0J492WdLCar4BS/KE3cRwAAACBAN0uSDyJqYLRsfYcFn4HyVf6TJxQm1IcwEt6GcJVzgjri9VtW7FqY5iBqa9B9Zdh5XXAYJ0XLsWQCcrmMHM2XGHGpA4gL9VlCJ/0QvOcXxD2uK7IXwAVUA7g4V4bw8EVnFv2Flufozhsp+4soo1xiYc5jiFVHwVlk21sMhAtKAeF' # 1024 Bit ] @sample_lines = [ "ssh-rsa #{@sample_rsa_keys[1]} root@someotherhost", "ssh-dss #{@sample_dsa_keys[0]} root@anywhere", "ssh-rsa #{@sample_rsa_keys[2]} paul" ] end after :each do Puppet::Type::Ssh_authorized_key::ProviderParsed.clear # Work around bug #6628 end def create_fake_key(username, content) filename = (username == :root ? @fake_rootfile : @fake_userfile ) File.open(filename, 'w') do |f| content.each do |line| f.puts line end end end def check_fake_key(username, expected_content) filename = (username == :root ? @fake_rootfile : @fake_userfile ) content = File.readlines(filename).map(&:chomp).sort.reject{ |x| x =~ /^#|^$/ } content.join("\n").should == expected_content.sort.join("\n") end def run_in_catalog(*resources) Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to the filebucket catalog = Puppet::Resource::Catalog.new catalog.host_config = false resources.each do |resource| resource.expects(:err).never catalog.add_resource(resource) end catalog.apply end describe "when managing one resource" do before :each do # We are not running as root so chown/chmod is not possible File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields end describe "with ensure set to absent" do before :each do @example = Puppet::Type.type(:ssh_authorized_key).new( :name => 'root@hostname', :type => :rsa, :key => @sample_rsa_keys[0], :target => @fake_rootfile, :user => 'root', :ensure => :absent ) end it "should not modify root's keyfile if resource is currently not present" do create_fake_key(:root, @sample_lines) run_in_catalog(@example) check_fake_key(:root, @sample_lines) end it "remove the key from root's keyfile if resource is currently present" do create_fake_key(:root, @sample_lines + ["ssh-rsa #{@sample_rsa_keys[0]} root@hostname"]) run_in_catalog(@example) check_fake_key(:root, @sample_lines) end end describe "when ensure is present" do before :each do @example = Puppet::Type.type(:ssh_authorized_key).new( :name => 'root@hostname', :type => :rsa, :key => @sample_rsa_keys[0], :target => @fake_rootfile, :user => 'root', :ensure => :present ) # just a dummy so the parsedfile provider is aware # of the user's authorized_keys file @dummy = Puppet::Type.type(:ssh_authorized_key).new( :name => 'dummy', :target => @fake_userfile, :user => 'nobody', :ensure => :absent ) end it "should add the key if it is not present" do create_fake_key(:root, @sample_lines) run_in_catalog(@example) check_fake_key(:root, @sample_lines + ["ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) end it "should modify the type if type is out of sync" do create_fake_key(:root,@sample_lines + [ "ssh-dss #{@sample_rsa_keys[0]} root@hostname" ]) run_in_catalog(@example) check_fake_key(:root, @sample_lines + [ "ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) end it "should modify the key if key is out of sync" do create_fake_key(:root,@sample_lines + [ "ssh-rsa #{@sample_rsa_keys[1]} root@hostname" ]) run_in_catalog(@example) check_fake_key(:root, @sample_lines + [ "ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) end it "should remove the key from old file if target is out of sync" do create_fake_key(:user, [ @sample_lines[0], "ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) create_fake_key(:root, [ @sample_lines[1], @sample_lines[2] ]) run_in_catalog(@example, @dummy) check_fake_key(:user, [ @sample_lines[0] ]) #check_fake_key(:root, [ @sample_lines[1], @sample_lines[2], "ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) end it "should add the key to new file if target is out of sync" do create_fake_key(:user, [ @sample_lines[0], "ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) create_fake_key(:root, [ @sample_lines[1], @sample_lines[2] ]) run_in_catalog(@example, @dummy) #check_fake_key(:user, [ @sample_lines[0] ]) check_fake_key(:root, [ @sample_lines[1], @sample_lines[2], "ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) end it "should modify options if options are out of sync" do @example[:options]=[ 'from="correct.domain.com"', 'no-port-forwarding', 'no-pty' ] create_fake_key(:root, @sample_lines + [ "from=\"incorrect.domain.com\",no-port-forwarding,no-pty ssh-rsa #{@sample_rsa_keys[0]} root@hostname"]) run_in_catalog(@example) check_fake_key(:root, @sample_lines + [ "from=\"correct.domain.com\",no-port-forwarding,no-pty ssh-rsa #{@sample_rsa_keys[0]} root@hostname"] ) end end end describe "when managing two resource" do before :each do # We are not running as root so chown/chmod is not possible File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields @example_one = Puppet::Type.type(:ssh_authorized_key).new( :name => 'root@hostname', :type => :rsa, :key => @sample_rsa_keys[0], :target => @fake_rootfile, :user => 'root', :ensure => :present ) @example_two = Puppet::Type.type(:ssh_authorized_key).new( :name => 'user@hostname', :key => @sample_rsa_keys[1], :type => :rsa, :target => @fake_userfile, :user => 'nobody', :ensure => :present ) end describe "and both keys are absent" do before :each do create_fake_key(:root, @sample_lines) create_fake_key(:user, @sample_lines) end it "should add both keys" do run_in_catalog(@example_one, @example_two) check_fake_key(:root, @sample_lines + [ "ssh-rsa #{@sample_rsa_keys[0]} root@hostname" ]) check_fake_key(:user, @sample_lines + [ "ssh-rsa #{@sample_rsa_keys[1]} user@hostname" ]) end end end end diff --git a/spec/integration/resource/type_collection_spec.rb b/spec/integration/resource/type_collection_spec.rb index 6ea2e7fe7..0852a9850 100755 --- a/spec/integration/resource/type_collection_spec.rb +++ b/spec/integration/resource/type_collection_spec.rb @@ -1,95 +1,95 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet/resource/type_collection' -describe Puppet::Resource::TypeCollection do +describe Puppet::Resource::TypeCollection, :fails_on_windows => true do describe "when autoloading from modules" do include PuppetSpec::Files before do @dir = tmpfile("autoload_testing") Puppet[:modulepath] = @dir FileUtils.mkdir_p @dir @code = Puppet::Resource::TypeCollection.new("env") Puppet::Node::Environment.new("env").stubs(:known_resource_types).returns @code end # Setup a module. def mk_module(name, files = {}) mdir = File.join(@dir, name) mandir = File.join(mdir, "manifests") FileUtils.mkdir_p mandir defs = files.delete(:define) Dir.chdir(mandir) do files.each do |file, classes| File.open("#{file}.pp", "w") do |f| classes.each { |klass| if defs f.puts "define #{klass} {}" else f.puts "class #{klass} {}" end } end end end end it "should return nil when a class can't be found or loaded" do @code.find_hostclass('', 'nosuchclass').should be_nil end it "should load the module's init file first" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("", name).name.should == name end it "should load the module's init file even when searching from a different namespace" do name = "simple" mk_module(name, :init => [name]) @code.find_hostclass("other::ns", name).name.should == name end it "should be able to load definitions from the module base file" do name = "simpdef" mk_module(name, :define => true, :init => [name]) @code.find_definition("", name).name.should == name end it "should be able to load qualified classes from the module base file" do modname = "both" name = "sub" mk_module(modname, :init => %w{both both::sub}) @code.find_hostclass("both", name).name.should == "both::sub" end it "should be able load classes from a separate file" do modname = "separate" name = "sub" mk_module(modname, :init => %w{separate}, :sub => %w{separate::sub}) @code.find_hostclass("separate", name).name.should == "separate::sub" end it "should not fail when loading from a separate file if there is no module file" do modname = "alone" name = "sub" mk_module(modname, :sub => %w{alone::sub}) lambda { @code.find_hostclass("alone", name) }.should_not raise_error end it "should be able to load definitions from their own file" do name = "mymod" mk_module(name, :define => true, :mydefine => ["mymod::mydefine"]) @code.find_definition("", "mymod::mydefine").name.should == "mymod::mydefine" end end end diff --git a/spec/integration/ssl/certificate_authority_spec.rb b/spec/integration/ssl/certificate_authority_spec.rb index c5e145459..c6ff58ed0 100755 --- a/spec/integration/ssl/certificate_authority_spec.rb +++ b/spec/integration/ssl/certificate_authority_spec.rb @@ -1,134 +1,134 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-17. # Copyright (c) 2008. All rights reserved. require 'spec_helper' require 'puppet/ssl/certificate_authority' require 'tempfile' -describe Puppet::SSL::CertificateAuthority do +describe Puppet::SSL::CertificateAuthority, :fails_on_windows => true do before do # Get a safe temporary file file = Tempfile.new("ca_integration_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local @ca = Puppet::SSL::CertificateAuthority.new end after { Puppet::SSL::Host.ca_location = :none system("rm -rf #{@dir}") Puppet.settings.clear Puppet::Util::Cacher.expire Puppet::SSL::CertificateAuthority.instance_variable_set("@instance", nil) } it "should create a CA host" do @ca.host.should be_ca end it "should be able to generate a certificate" do @ca.generate_ca_certificate @ca.host.certificate.should be_instance_of(Puppet::SSL::Certificate) end it "should be able to generate a new host certificate" do @ca.generate("newhost") Puppet::SSL::Certificate.indirection.find("newhost").should be_instance_of(Puppet::SSL::Certificate) end it "should be able to revoke a host certificate" do @ca.generate("newhost") @ca.revoke("newhost") lambda { @ca.verify("newhost") }.should raise_error end it "should have a CRL" do @ca.generate_ca_certificate @ca.crl.should_not be_nil end it "should be able to read in a previously created CRL" do @ca.generate_ca_certificate # Create it to start with. @ca.crl Puppet::SSL::CertificateAuthority.new.crl.should_not be_nil end describe "when signing certificates" do before do @host = Puppet::SSL::Host.new("luke.madstop.com") # We have to provide the key, since when we're in :ca_only mode, we can only interact # with the CA key. key = Puppet::SSL::Key.new(@host.name) key.generate @host.key = key @host.generate_certificate_request path = File.join(Puppet[:requestdir], "luke.madstop.com.pem") end it "should be able to sign certificates" do @ca.sign("luke.madstop.com") end it "should save the signed certificate" do @ca.sign("luke.madstop.com") Puppet::SSL::Certificate.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::Certificate) end it "should be able to sign multiple certificates" do @other = Puppet::SSL::Host.new("other.madstop.com") okey = Puppet::SSL::Key.new(@other.name) okey.generate @other.key = okey @other.generate_certificate_request @ca.sign("luke.madstop.com") @ca.sign("other.madstop.com") Puppet::SSL::Certificate.indirection.find("other.madstop.com").should be_instance_of(Puppet::SSL::Certificate) Puppet::SSL::Certificate.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::Certificate) end it "should save the signed certificate to the :signeddir" do @ca.sign("luke.madstop.com") client_cert = File.join(Puppet[:signeddir], "luke.madstop.com.pem") File.read(client_cert).should == Puppet::SSL::Certificate.indirection.find("luke.madstop.com").content.to_s end it "should save valid certificates" do @ca.sign("luke.madstop.com") unless ssl = Puppet::Util::which('openssl') pending "No ssl available" else ca_cert = Puppet[:cacert] client_cert = File.join(Puppet[:signeddir], "luke.madstop.com.pem") output = %x{openssl verify -CAfile #{ca_cert} #{client_cert}} $CHILD_STATUS.should == 0 end end end end diff --git a/spec/integration/ssl/certificate_request_spec.rb b/spec/integration/ssl/certificate_request_spec.rb index 688466c37..31bb48d66 100755 --- a/spec/integration/ssl/certificate_request_spec.rb +++ b/spec/integration/ssl/certificate_request_spec.rb @@ -1,62 +1,62 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-17. # Copyright (c) 2008. All rights reserved. require 'spec_helper' require 'puppet/ssl/certificate_request' require 'tempfile' -describe Puppet::SSL::CertificateRequest do +describe Puppet::SSL::CertificateRequest, :fails_on_windows => true do before do # Get a safe temporary file file = Tempfile.new("csr_integration_testing") @dir = file.path file.delete Dir.mkdir(@dir) Puppet.settings.clear Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :none @csr = Puppet::SSL::CertificateRequest.new("luke.madstop.com") @key = OpenSSL::PKey::RSA.new(512) end after do system("rm -rf #{@dir}") Puppet.settings.clear # This is necessary so the terminus instances don't lie around. Puppet::Util::Cacher.expire end it "should be able to generate CSRs" do @csr.generate(@key) end it "should be able to save CSRs" do Puppet::SSL::CertificateRequest.indirection.save(@csr) end it "should be able to find saved certificate requests via the Indirector" do @csr.generate(@key) Puppet::SSL::CertificateRequest.indirection.save(@csr) Puppet::SSL::CertificateRequest.indirection.find("luke.madstop.com").should be_instance_of(Puppet::SSL::CertificateRequest) end it "should save the completely CSR when saving" do @csr.generate(@key) Puppet::SSL::CertificateRequest.indirection.save(@csr) Puppet::SSL::CertificateRequest.indirection.find("luke.madstop.com").content.to_s.should == @csr.content.to_s end end diff --git a/spec/integration/ssl/certificate_revocation_list_spec.rb b/spec/integration/ssl/certificate_revocation_list_spec.rb index 051a81569..95f0e6314 100755 --- a/spec/integration/ssl/certificate_revocation_list_spec.rb +++ b/spec/integration/ssl/certificate_revocation_list_spec.rb @@ -1,43 +1,43 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2008-5-5. # Copyright (c) 2008. All rights reserved. require 'spec_helper' require 'puppet/ssl/certificate_revocation_list' require 'tempfile' -describe Puppet::SSL::CertificateRevocationList do +describe Puppet::SSL::CertificateRevocationList, :fails_on_windows => true do before do # Get a safe temporary file file = Tempfile.new("ca_integration_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local end after { Puppet::SSL::Host.ca_location = :none system("rm -rf #{@dir}") Puppet.settings.clear # This is necessary so the terminus instances don't lie around. Puppet::Util::Cacher.expire } it "should be able to read in written out CRLs with no revoked certificates" do ca = Puppet::SSL::CertificateAuthority.new raise "CRL not created" unless FileTest.exist?(Puppet[:hostcrl]) crl = Puppet::SSL::CertificateRevocationList.new("crl_int_testing") crl.read(Puppet[:hostcrl]) end end diff --git a/spec/integration/ssl/host_spec.rb b/spec/integration/ssl/host_spec.rb index e9c37c151..2da5bcfe3 100755 --- a/spec/integration/ssl/host_spec.rb +++ b/spec/integration/ssl/host_spec.rb @@ -1,91 +1,91 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2008-4-17. # Copyright (c) 2008. All rights reserved. require 'spec_helper' require 'puppet/ssl/host' require 'tempfile' -describe Puppet::SSL::Host do +describe Puppet::SSL::Host, :fails_on_windows => true do before do # Get a safe temporary file file = Tempfile.new("host_integration_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir Puppet.settings[:group] = Process.gid Puppet::SSL::Host.ca_location = :local @host = Puppet::SSL::Host.new("luke.madstop.com") @ca = Puppet::SSL::CertificateAuthority.new end after { Puppet::SSL::Host.ca_location = :none system("rm -rf #{@dir}") Puppet.settings.clear Puppet::Util::Cacher.expire } it "should be considered a CA host if its name is equal to 'ca'" do Puppet::SSL::Host.new(Puppet::SSL::CA_NAME).should be_ca end describe "when managing its key" do it "should be able to generate and save a key" do @host.generate_key end it "should save the key such that the Indirector can find it" do @host.generate_key Puppet::SSL::Key.indirection.find(@host.name).content.to_s.should == @host.key.to_s end it "should save the private key into the :privatekeydir" do @host.generate_key File.read(File.join(Puppet.settings[:privatekeydir], "luke.madstop.com.pem")).should == @host.key.to_s end end describe "when managing its certificate request" do it "should be able to generate and save a certificate request" do @host.generate_certificate_request end it "should save the certificate request such that the Indirector can find it" do @host.generate_certificate_request Puppet::SSL::CertificateRequest.indirection.find(@host.name).content.to_s.should == @host.certificate_request.to_s end it "should save the private certificate request into the :privatekeydir" do @host.generate_certificate_request File.read(File.join(Puppet.settings[:requestdir], "luke.madstop.com.pem")).should == @host.certificate_request.to_s end end describe "when the CA host" do it "should never store its key in the :privatekeydir" do Puppet.settings.use(:main, :ssl, :ca) @ca = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name) @ca.generate_key FileTest.should_not be_exist(File.join(Puppet[:privatekeydir], "ca.pem")) end end it "should pass the verification of its own SSL store" do @host.generate @ca = Puppet::SSL::CertificateAuthority.new @ca.sign(@host.name) @host.ssl_store.verify(@host.certificate.content).should be_true end end diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb index 00e9dbb8e..b4214214e 100755 --- a/spec/integration/transaction_spec.rb +++ b/spec/integration/transaction_spec.rb @@ -1,340 +1,338 @@ #!/usr/bin/env rspec require 'spec_helper' -require 'puppet_spec/files' require 'puppet/transaction' -require 'puppet_spec/files' describe Puppet::Transaction do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end def mk_catalog(*resources) catalog = Puppet::Resource::Catalog.new(Puppet::Node.new("mynode")) resources.each { |res| catalog.add_resource res } catalog end it "should not apply generated resources if the parent resource fails" do catalog = Puppet::Resource::Catalog.new - resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false + resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false catalog.add_resource resource - child_resource = Puppet::Type.type(:file).new :path => "/foo/bar/baz", :backup => false + child_resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar/baz"), :backup => false resource.expects(:eval_generate).returns([child_resource]) transaction = Puppet::Transaction.new(catalog) resource.expects(:retrieve).raises "this is a failure" resource.stubs(:err) child_resource.expects(:retrieve).never transaction.evaluate end it "should not apply virtual resources" do catalog = Puppet::Resource::Catalog.new - resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false + resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false resource.virtual = true catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) resource.expects(:evaluate).never transaction.evaluate end it "should apply exported resources" do catalog = Puppet::Resource::Catalog.new path = tmpfile("exported_files") resource = Puppet::Type.type(:file).new :path => path, :backup => false, :ensure => :file resource.exported = true catalog.add_resource resource catalog.apply FileTest.should be_exist(path) end it "should not apply virtual exported resources" do catalog = Puppet::Resource::Catalog.new - resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false + resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false resource.exported = true resource.virtual = true catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) resource.expects(:evaluate).never transaction.evaluate end it "should not apply device resources on normal host" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:interface).new :name => "FastEthernet 0/1" catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = false transaction.expects(:apply).never.with(resource, nil) transaction.evaluate transaction.resource_status(resource).should be_skipped end it "should not apply host resources on device" do catalog = Puppet::Resource::Catalog.new - resource = Puppet::Type.type(:file).new :path => "/foo/bar", :backup => false + resource = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar"), :backup => false catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = true transaction.expects(:apply).never.with(resource, nil) transaction.evaluate transaction.resource_status(resource).should be_skipped end it "should apply device resources on device" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:interface).new :name => "FastEthernet 0/1" catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = true transaction.expects(:apply).with(resource, nil) transaction.evaluate transaction.resource_status(resource).should_not be_skipped end it "should apply resources appliable on host and device on a device" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:schedule).new :name => "test" catalog.add_resource resource transaction = Puppet::Transaction.new(catalog) transaction.for_network_device = true transaction.expects(:apply).with(resource, nil) transaction.evaluate transaction.resource_status(resource).should_not be_skipped end # Verify that one component requiring another causes the contained # resources in the requiring component to get refreshed. - it "should propagate events from a contained resource through its container to its dependent container's contained resources" do + it "should propagate events from a contained resource through its container to its dependent container's contained resources", :fails_on_windows => true do transaction = nil file = Puppet::Type.type(:file).new :path => tmpfile("event_propagation"), :ensure => :present execfile = File.join(tmpdir("exec_event"), "exectestingness2") exec = Puppet::Type.type(:exec).new :command => "touch #{execfile}", :path => ENV['PATH'] catalog = mk_catalog(file) fcomp = Puppet::Type.type(:component).new(:name => "Foo[file]") catalog.add_resource fcomp catalog.add_edge(fcomp, file) ecomp = Puppet::Type.type(:component).new(:name => "Foo[exec]") catalog.add_resource ecomp catalog.add_resource exec catalog.add_edge(ecomp, exec) ecomp[:subscribe] = Puppet::Resource.new(:foo, "file") exec[:refreshonly] = true exec.expects(:refresh) catalog.apply end # Make sure that multiple subscriptions get triggered. - it "should propagate events to all dependent resources" do + it "should propagate events to all dependent resources", :fails_on_windows => true do path = tmpfile("path") file1 = tmpfile("file1") file2 = tmpfile("file2") file = Puppet::Type.type(:file).new( :path => path, :ensure => "file" ) exec1 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch #{file1}", :refreshonly => true, :subscribe => Puppet::Resource.new(:file, path) ) exec2 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch #{file2}", :refreshonly => true, :subscribe => Puppet::Resource.new(:file, path) ) catalog = mk_catalog(file, exec1, exec2) catalog.apply FileTest.should be_exist(file1) FileTest.should be_exist(file2) end - it "should not let one failed refresh result in other refreshes failing" do + it "should not let one failed refresh result in other refreshes failing", :fails_on_windows => true do path = tmpfile("path") newfile = tmpfile("file") file = Puppet::Type.type(:file).new( :path => path, :ensure => "file" ) exec1 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch /this/cannot/possibly/exist", :logoutput => true, :refreshonly => true, :subscribe => file, :title => "one" ) exec2 = Puppet::Type.type(:exec).new( :path => ENV["PATH"], :command => "touch #{newfile}", :logoutput => true, :refreshonly => true, :subscribe => [file, exec1], :title => "two" ) exec1.stubs(:err) catalog = mk_catalog(file, exec1, exec2) catalog.apply FileTest.should be_exists(newfile) end - it "should still trigger skipped resources", :'fails_on_ruby_1.9.2' => true do + it "should still trigger skipped resources", :'fails_on_ruby_1.9.2' => true, :fails_on_windows => true do catalog = mk_catalog catalog.add_resource(*Puppet::Type.type(:schedule).mkdefaultschedules) Puppet[:ignoreschedules] = false file = Puppet::Type.type(:file).new( :name => tmpfile("file"), :ensure => "file", :backup => false ) fname = tmpfile("exec") exec = Puppet::Type.type(:exec).new( :name => "touch #{fname}", :path => "/usr/bin:/bin", :schedule => "monthly", :subscribe => Puppet::Resource.new("file", file.name) ) catalog.add_resource(file, exec) # Run it once catalog.apply FileTest.should be_exists(fname) # Now remove it, so it can get created again File.unlink(fname) file[:content] = "some content" catalog.apply FileTest.should be_exists(fname) # Now remove it, so it can get created again File.unlink(fname) # And tag our exec exec.tag("testrun") # And our file, so it runs file.tag("norun") Puppet[:tags] = "norun" file[:content] = "totally different content" catalog.apply FileTest.should be_exists(fname) end - it "should not attempt to evaluate resources with failed dependencies" do + it "should not attempt to evaluate resources with failed dependencies", :fails_on_windows => true do exec = Puppet::Type.type(:exec).new( :command => "/bin/mkdir /this/path/cannot/possibly/exist", :title => "mkdir" ) file1 = Puppet::Type.type(:file).new( :title => "file1", :path => tmpfile("file1"), :require => exec, :ensure => :file ) file2 = Puppet::Type.type(:file).new( :title => "file2", :path => tmpfile("file2"), :require => file1, :ensure => :file ) catalog = mk_catalog(exec, file1, file2) catalog.apply FileTest.should_not be_exists(file1[:path]) FileTest.should_not be_exists(file2[:path]) end - it "should not trigger subscribing resources on failure" do + it "should not trigger subscribing resources on failure", :fails_on_windows => true do file1 = tmpfile("file1") file2 = tmpfile("file2") create_file1 = Puppet::Type.type(:exec).new( :command => "/usr/bin/touch #{file1}" ) exec = Puppet::Type.type(:exec).new( :command => "/bin/mkdir /this/path/cannot/possibly/exist", :title => "mkdir", :notify => create_file1 ) create_file2 = Puppet::Type.type(:exec).new( :command => "/usr/bin/touch #{file2}", :subscribe => exec ) catalog = mk_catalog(exec, create_file1, create_file2) catalog.apply FileTest.should_not be_exists(file1) FileTest.should_not be_exists(file2) end # #801 -- resources only checked in noop should be rescheduled immediately. it "should immediately reschedule noop resources" do Puppet::Type.type(:schedule).mkdefaultschedules resource = Puppet::Type.type(:notify).new(:name => "mymessage", :noop => true) catalog = Puppet::Resource::Catalog.new catalog.add_resource resource trans = catalog.apply trans.resource_harness.should be_scheduled(trans.resource_status(resource), resource) end end diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb index 4bed8c6c1..241861cc5 100755 --- a/spec/integration/type/file_spec.rb +++ b/spec/integration/type/file_spec.rb @@ -1,511 +1,511 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet_spec/files' describe Puppet::Type.type(:file) do include PuppetSpec::Files before do # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) end it "should not attempt to manage files that do not exist if no means of creating the file is specified" do - file = Puppet::Type.type(:file).new :path => "/my/file", :mode => "755" + file = Puppet::Type.type(:file).new :path => make_absolute("/my/file"), :mode => "755" catalog = Puppet::Resource::Catalog.new catalog.add_resource file file.parameter(:mode).expects(:retrieve).never transaction = Puppet::Transaction.new(catalog) transaction.resource_harness.evaluate(file).should_not be_failed end describe "when writing files" do - it "should backup files to a filebucket when one is configured" do + it "should backup files to a filebucket when one is configured", :fails_on_windows => true do bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => "mybucket", :content => "foo" catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.add_resource bucket File.open(file[:path], "w") { |f| f.puts "bar" } md5 = Digest::MD5.hexdigest(File.read(file[:path])) catalog.apply bucket.bucket.getfile(md5).should == "bar\n" end it "should backup files in the local directory when a backup string is provided" do file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => ".bak", :content => "foo" catalog = Puppet::Resource::Catalog.new catalog.add_resource file File.open(file[:path], "w") { |f| f.puts "bar" } catalog.apply backup = file[:path] + ".bak" FileTest.should be_exist(backup) File.read(backup).should == "bar\n" end it "should fail if no backup can be performed" do dir = tmpfile("backups") Dir.mkdir(dir) path = File.join(dir, "testfile") file = Puppet::Type.type(:file).new :path => path, :backup => ".bak", :content => "foo" catalog = Puppet::Resource::Catalog.new catalog.add_resource file File.open(file[:path], "w") { |f| f.puts "bar" } # Create a directory where the backup should be so that writing to it fails Dir.mkdir(File.join(dir, "testfile.bak")) Puppet::Util::Log.stubs(:newmessage) catalog.apply File.read(file[:path]).should == "bar\n" end - it "should not backup symlinks" do + it "should not backup symlinks", :fails_on_windows => true do link = tmpfile("link") dest1 = tmpfile("dest1") dest2 = tmpfile("dest2") bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = Puppet::Type.type(:file).new :path => link, :target => dest2, :ensure => :link, :backup => "mybucket" catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.add_resource bucket File.open(dest1, "w") { |f| f.puts "whatever" } File.symlink(dest1, link) md5 = Digest::MD5.hexdigest(File.read(file[:path])) catalog.apply File.readlink(link).should == dest2 Find.find(bucket[:path]) { |f| File.file?(f) }.should be_nil end it "should backup directories to the local filesystem by copying the whole directory" do file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => ".bak", :content => "foo", :force => true catalog = Puppet::Resource::Catalog.new catalog.add_resource file Dir.mkdir(file[:path]) otherfile = File.join(file[:path], "foo") File.open(otherfile, "w") { |f| f.print "yay" } catalog.apply backup = file[:path] + ".bak" FileTest.should be_directory(backup) File.read(File.join(backup, "foo")).should == "yay" end - it "should backup directories to filebuckets by backing up each file separately" do + it "should backup directories to filebuckets by backing up each file separately", :fails_on_windows => true do bucket = Puppet::Type.type(:filebucket).new :path => tmpfile("filebucket"), :name => "mybucket" file = Puppet::Type.type(:file).new :path => tmpfile("bucket_backs"), :backup => "mybucket", :content => "foo", :force => true catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.add_resource bucket Dir.mkdir(file[:path]) foofile = File.join(file[:path], "foo") barfile = File.join(file[:path], "bar") File.open(foofile, "w") { |f| f.print "fooyay" } File.open(barfile, "w") { |f| f.print "baryay" } foomd5 = Digest::MD5.hexdigest(File.read(foofile)) barmd5 = Digest::MD5.hexdigest(File.read(barfile)) catalog.apply bucket.bucket.getfile(foomd5).should == "fooyay" bucket.bucket.getfile(barmd5).should == "baryay" end it "should propagate failures encountered when renaming the temporary file" do file = Puppet::Type.type(:file).new :path => tmpfile("fail_rename"), :content => "foo" file.stubs(:remove_existing) # because it tries to make a backup catalog = Puppet::Resource::Catalog.new catalog.add_resource file File.open(file[:path], "w") { |f| f.print "bar" } File.expects(:rename).raises ArgumentError lambda { file.write(:content) }.should raise_error(Puppet::Error) File.read(file[:path]).should == "bar" end end describe "when recursing" do def build_path(dir) Dir.mkdir(dir) File.chmod(0750, dir) @dirs = [dir] @files = [] %w{one two}.each do |subdir| fdir = File.join(dir, subdir) Dir.mkdir(fdir) File.chmod(0750, fdir) @dirs << fdir %w{three}.each do |file| ffile = File.join(fdir, file) @files << ffile File.open(ffile, "w") { |f| f.puts "test #{file}" } File.chmod(0640, ffile) end end end - it "should be able to recurse over a nonexistent file" do + it "should be able to recurse over a nonexistent file", :fails_on_windows => true do @path = tmpfile("file_integration_tests") @file = Puppet::Type::File.new( :name => @path, :mode => 0644, :recurse => true, :backup => false ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file lambda { @file.eval_generate }.should_not raise_error end it "should be able to recursively set properties on existing files" do @path = tmpfile("file_integration_tests") build_path(@path) @file = Puppet::Type::File.new( :name => @path, :mode => 0644, :recurse => true, :backup => false ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @dirs.each do |path| (File.stat(path).mode & 007777).should == 0755 end @files.each do |path| (File.stat(path).mode & 007777).should == 0644 end end - it "should be able to recursively make links to other files" do + it "should be able to recursively make links to other files", :fails_on_windows => true do source = tmpfile("file_link_integration_source") build_path(source) dest = tmpfile("file_link_integration_dest") @file = Puppet::Type::File.new(:name => dest, :target => source, :recurse => true, :ensure => :link, :backup => false) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @dirs.each do |path| link_path = path.sub(source, dest) File.lstat(link_path).should be_directory end @files.each do |path| link_path = path.sub(source, dest) File.lstat(link_path).ftype.should == "link" end end - it "should be able to recursively copy files" do + it "should be able to recursively copy files", :fails_on_windows => true do source = tmpfile("file_source_integration_source") build_path(source) dest = tmpfile("file_source_integration_dest") @file = Puppet::Type::File.new(:name => dest, :source => source, :recurse => true, :backup => false) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file @catalog.apply @dirs.each do |path| newpath = path.sub(source, dest) File.lstat(newpath).should be_directory end @files.each do |path| newpath = path.sub(source, dest) File.lstat(newpath).ftype.should == "file" end end it "should not recursively manage files managed by a more specific explicit file" do dir = tmpfile("recursion_vs_explicit_1") subdir = File.join(dir, "subdir") file = File.join(subdir, "file") FileUtils.mkdir_p(subdir) File.open(file, "w") { |f| f.puts "" } base = Puppet::Type::File.new(:name => dir, :recurse => true, :backup => false, :mode => "755") sub = Puppet::Type::File.new(:name => subdir, :recurse => true, :backup => false, :mode => "644") @catalog = Puppet::Resource::Catalog.new @catalog.add_resource base @catalog.add_resource sub @catalog.apply (File.stat(file).mode & 007777).should == 0644 end - it "should recursively manage files even if there is an explicit file whose name is a prefix of the managed file" do + it "should recursively manage files even if there is an explicit file whose name is a prefix of the managed file", :fails_on_windows => true do dir = tmpfile("recursion_vs_explicit_2") managed = File.join(dir, "file") generated = File.join(dir, "file_with_a_name_starting_with_the_word_file") FileUtils.mkdir_p(dir) File.open(managed, "w") { |f| f.puts "" } File.open(generated, "w") { |f| f.puts "" } @catalog = Puppet::Resource::Catalog.new @catalog.add_resource Puppet::Type::File.new(:name => dir, :recurse => true, :backup => false, :mode => "755") @catalog.add_resource Puppet::Type::File.new(:name => managed, :recurse => true, :backup => false, :mode => "644") @catalog.apply (File.stat(generated).mode & 007777).should == 0755 end end - describe "when generating resources" do + describe "when generating resources", :fails_on_windows => true do before do @source = tmpfile("generating_in_catalog_source") @dest = tmpfile("generating_in_catalog_dest") Dir.mkdir(@source) s1 = File.join(@source, "one") s2 = File.join(@source, "two") File.open(s1, "w") { |f| f.puts "uno" } File.open(s2, "w") { |f| f.puts "dos" } @file = Puppet::Type::File.new(:name => @dest, :source => @source, :recurse => true, :backup => false) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @file end it "should add each generated resource to the catalog" do @catalog.apply do |trans| @catalog.resource(:file, File.join(@dest, "one")).should be_instance_of(@file.class) @catalog.resource(:file, File.join(@dest, "two")).should be_instance_of(@file.class) end end it "should have an edge to each resource in the relationship graph" do @catalog.apply do |trans| one = @catalog.resource(:file, File.join(@dest, "one")) @catalog.relationship_graph.edge?(@file, one).should be two = @catalog.resource(:file, File.join(@dest, "two")) @catalog.relationship_graph.edge?(@file, two).should be end end end describe "when copying files" do # Ticket #285. - it "should be able to copy files with pound signs in their names" do + it "should be able to copy files with pound signs in their names", :fails_on_windows => true do source = tmpfile("filewith#signs") dest = tmpfile("destwith#signs") File.open(source, "w") { |f| f.print "foo" } file = Puppet::Type::File.new(:name => dest, :source => source) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "foo" end - it "should be able to copy files with spaces in their names" do + it "should be able to copy files with spaces in their names", :fails_on_windows => true do source = tmpfile("filewith spaces") dest = tmpfile("destwith spaces") File.open(source, "w") { |f| f.print "foo" } File.chmod(0755, source) file = Puppet::Type::File.new(:path => dest, :source => source) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "foo" (File.stat(dest).mode & 007777).should == 0755 end - it "should be able to copy individual files even if recurse has been specified" do + it "should be able to copy individual files even if recurse has been specified", :fails_on_windows => true do source = tmpfile("source") dest = tmpfile("dest") File.open(source, "w") { |f| f.print "foo" } file = Puppet::Type::File.new(:name => dest, :source => source, :recurse => true) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "foo" end end it "should be able to create files when 'content' is specified but 'ensure' is not" do dest = tmpfile("files_with_content") file = Puppet::Type.type(:file).new( :name => dest, :content => "this is some content, yo" ) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "this is some content, yo" end it "should create files with content if both 'content' and 'ensure' are set" do dest = tmpfile("files_with_content") file = Puppet::Type.type(:file).new( :name => dest, :ensure => "file", :content => "this is some content, yo" ) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.read(dest).should == "this is some content, yo" end - it "should delete files with sources but that are set for deletion" do + it "should delete files with sources but that are set for deletion", :fails_on_windows => true do dest = tmpfile("dest_source_with_ensure") source = tmpfile("source_source_with_ensure") File.open(source, "w") { |f| f.puts "yay" } File.open(dest, "w") { |f| f.puts "boo" } file = Puppet::Type.type(:file).new( - :name => dest, + :name => make_absolute(dest), :ensure => :absent, :source => source, :backup => false ) catalog = Puppet::Resource::Catalog.new catalog.add_resource file catalog.apply File.should_not be_exist(dest) end - describe "when purging files" do + describe "when purging files", :fails_on_windows => true do before do @sourcedir = tmpfile("purge_source") @destdir = tmpfile("purge_dest") Dir.mkdir(@sourcedir) Dir.mkdir(@destdir) @sourcefile = File.join(@sourcedir, "sourcefile") @copiedfile = File.join(@destdir, "sourcefile") @localfile = File.join(@destdir, "localfile") @purgee = File.join(@destdir, "to_be_purged") File.open(@localfile, "w") { |f| f.puts "rahtest" } File.open(@sourcefile, "w") { |f| f.puts "funtest" } # this file should get removed File.open(@purgee, "w") { |f| f.puts "footest" } @lfobj = Puppet::Type.newfile( :title => "localfile", :path => @localfile, :content => "rahtest\n", :ensure => :file, :backup => false ) @destobj = Puppet::Type.newfile( :title => "destdir", :path => @destdir, :source => @sourcedir, :backup => false, :purge => true, :recurse => true ) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @lfobj, @destobj end it "should still copy remote files" do @catalog.apply FileTest.should be_exist(@copiedfile) end it "should not purge managed, local files" do @catalog.apply FileTest.should be_exist(@localfile) end it "should purge files that are neither remote nor otherwise managed" do @catalog.apply FileTest.should_not be_exist(@purgee) end end end diff --git a/spec/integration/type/tidy_spec.rb b/spec/integration/type/tidy_spec.rb index 675aaf4cd..08a24099c 100755 --- a/spec/integration/type/tidy_spec.rb +++ b/spec/integration/type/tidy_spec.rb @@ -1,31 +1,31 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet_spec/files' require 'puppet/file_bucket/dipper' describe Puppet::Type.type(:tidy) do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end # Testing #355. - it "should be able to remove dead links" do + it "should be able to remove dead links", :fails_on_windows => true do dir = tmpfile("tidy_link_testing") link = File.join(dir, "link") target = tmpfile("no_such_file_tidy_link_testing") Dir.mkdir(dir) File.symlink(target, link) tidy = Puppet::Type.type(:tidy).new :path => dir, :recurse => true catalog = Puppet::Resource::Catalog.new catalog.add_resource(tidy) catalog.apply FileTest.should_not be_symlink(link) end end diff --git a/spec/integration/util/autoload_spec.rb b/spec/integration/util/autoload_spec.rb index 92fc6554c..771e6a718 100755 --- a/spec/integration/util/autoload_spec.rb +++ b/spec/integration/util/autoload_spec.rb @@ -1,113 +1,113 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/autoload' require 'fileutils' class AutoloadIntegrator @things = [] def self.newthing(name) @things << name end def self.thing?(name) @things.include? name end def self.clear @things.clear end end require 'puppet_spec/files' describe Puppet::Util::Autoload do include PuppetSpec::Files def with_file(name, *path) path = File.join(*path) # Now create a file to load File.open(path, "w") { |f| f.puts "\nAutoloadIntegrator.newthing(:#{name.to_s})\n" } yield File.delete(path) end def with_loader(name, path) dir = tmpfile(name + path) $LOAD_PATH << dir Dir.mkdir(dir) rbdir = File.join(dir, path.to_s) Dir.mkdir(rbdir) loader = Puppet::Util::Autoload.new(name, path) yield rbdir, loader Dir.rmdir(rbdir) Dir.rmdir(dir) $LOAD_PATH.pop AutoloadIntegrator.clear end it "should make instances available by the loading class" do loader = Puppet::Util::Autoload.new("foo", "bar") Puppet::Util::Autoload["foo"].should == loader end it "should not fail when asked to load a missing file" do Puppet::Util::Autoload.new("foo", "bar").load(:eh).should be_false end it "should load and return true when it successfully loads a file" do with_loader("foo", "bar") { |dir,loader| with_file(:mything, dir, "mything.rb") { loader.load(:mything).should be_true loader.should be_loaded(:mything) AutoloadIntegrator.should be_thing(:mything) } } end it "should consider a file loaded when asked for the name without an extension" do with_loader("foo", "bar") { |dir,loader| with_file(:noext, dir, "noext.rb") { loader.load(:noext) loader.should be_loaded(:noext) } } end it "should consider a file loaded when asked for the name with an extension" do with_loader("foo", "bar") { |dir,loader| with_file(:noext, dir, "withext.rb") { loader.load(:withext) loader.should be_loaded("withext.rb") } } end it "should register the fact that the instance is loaded with the Autoload base class" do with_loader("foo", "bar") { |dir,loader| with_file(:baseload, dir, "baseload.rb") { loader.load(:baseload) Puppet::Util::Autoload.should be_loaded("bar/withext.rb") } } end - it "should be able to load files directly from modules" do + it "should be able to load files directly from modules", :fails_on_windows => true do modulepath = tmpfile("autoload_module_testing") libdir = File.join(modulepath, "mymod", "lib", "foo") FileUtils.mkdir_p(libdir) file = File.join(libdir, "plugin.rb") Puppet[:modulepath] = modulepath with_loader("foo", "foo") do |dir, loader| with_file(:plugin, file.split("/")) do loader.load(:plugin) loader.should be_loaded("plugin.rb") end end end end diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb index b05c63107..46d783c4e 100755 --- a/spec/integration/util/settings_spec.rb +++ b/spec/integration/util/settings_spec.rb @@ -1,29 +1,29 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet_spec/files' describe Puppet::Util::Settings do include PuppetSpec::Files def minimal_default_settings { :noop => {:default => false, :desc => "noop"} } end it "should be able to make needed directories" do settings = Puppet::Util::Settings.new settings.setdefaults :main, minimal_default_settings.update( :maindir => [tmpfile("main"), "a"] ) settings.use(:main) File.should be_directory(settings[:maindir]) end - it "should make its directories with the corret modes" do + it "should make its directories with the correct modes" do settings = Puppet::Util::Settings.new settings.setdefaults :main, minimal_default_settings.update( :maindir => {:default => tmpfile("main"), :desc => "a", :mode => 0750} ) settings.use(:main) - (File.stat(settings[:maindir]).mode & 007777).should == 0750 + (File.stat(settings[:maindir]).mode & 007777).should == (Puppet.features.microsoft_windows? ? 0755 : 0750) end end diff --git a/spec/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb index 30fb4fc42..9e75d3142 100755 --- a/spec/lib/puppet_spec/files.rb +++ b/spec/lib/puppet_spec/files.rb @@ -1,53 +1,63 @@ require 'fileutils' require 'tempfile' # A support module for testing files. module PuppetSpec::Files # This code exists only to support tests that run as root, pretty much. # Once they have finally been eliminated this can all go... --daniel 2011-04-08 if Puppet.features.posix? then def self.in_tmp(path) path =~ /^\/tmp/ or path =~ /^\/var\/folders/ end elsif Puppet.features.microsoft_windows? def self.in_tmp(path) tempdir = File.expand_path(File.join(Dir::LOCAL_APPDATA, "Temp")) path =~ /^#{tempdir}/ end else fail "Help! Can't find in_tmp for this platform" end def self.cleanup $global_tempfiles ||= [] while path = $global_tempfiles.pop do fail "Not deleting tmpfile #{path} outside regular tmpdir" unless in_tmp(path) begin FileUtils.rm_r path, :secure => true rescue Errno::ENOENT # nothing to do end end end + def make_absolute(path) + return path unless Puppet.features.microsoft_windows? + # REMIND UNC + return path if path =~ /^[A-Za-z]:/ + + pwd = Dir.getwd + return "#{pwd[0,2]}#{path}" if pwd.length > 2 and pwd =~ /^[A-Za-z]:/ + return "C:#{path}" + end + def tmpfile(name) # Generate a temporary file, just for the name... source = Tempfile.new(name) path = source.path source.close! # ...record it for cleanup, $global_tempfiles ||= [] $global_tempfiles << File.expand_path(path) # ...and bam. path end def tmpdir(name) path = tmpfile(name) FileUtils.mkdir_p(path) path end end diff --git a/spec/unit/application/device_spec.rb b/spec/unit/application/device_spec.rb index 42a62da22..f88c0c3d9 100755 --- a/spec/unit/application/device_spec.rb +++ b/spec/unit/application/device_spec.rb @@ -1,341 +1,343 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/application/device' require 'puppet/util/network_device/config' require 'ostruct' require 'puppet/configurer' describe Puppet::Application::Device do + include PuppetSpec::Files + before :each do @device = Puppet::Application[:device] @device.preinit Puppet::Util::Log.stubs(:newdestination) Puppet::Node.indirection.stubs(:terminus_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) end it "should operate in agent run_mode" do @device.class.run_mode.name.should == :agent end it "should ask Puppet::Application to parse Puppet configuration file" do @device.should_parse_config?.should be_true end it "should declare a main command" do @device.should respond_to(:main) end it "should declare a preinit block" do @device.should respond_to(:preinit) end describe "in preinit" do before :each do @device.stubs(:trap) end it "should catch INT" do Signal.expects(:trap).with { |arg,block| arg == :INT } @device.preinit end end describe "when handling options" do before do @device.command_line.stubs(:args).returns([]) end [:centrallogging, :debug, :verbose,].each do |option| it "should declare handle_#{option} method" do @device.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @device.options.expects(:[]=).with(option, 'arg') @device.send("handle_#{option}".to_sym, 'arg') end end it "should set waitforcert to 0 with --onetime and if --waitforcert wasn't given" do Puppet[:onetime] = true Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(0) @device.setup_host end it "should use supplied waitforcert when --onetime is specified" do Puppet[:onetime] = true @device.handle_waitforcert(60) Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(60) @device.setup_host end it "should use a default value for waitforcert when --onetime and --waitforcert are not specified" do Puppet::SSL::Host.any_instance.expects(:wait_for_cert).with(120) @device.setup_host end it "should set the log destination with --logdest" do @device.options.stubs(:[]=).with { |opt,val| opt == :setdest } Puppet::Log.expects(:newdestination).with("console") @device.handle_logdest("console") end it "should put the setdest options to true" do @device.options.expects(:[]=).with(:setdest,true) @device.handle_logdest("console") end it "should parse the log destination from the command line" do @device.command_line.stubs(:args).returns(%w{--logdest /my/file}) Puppet::Util::Log.expects(:newdestination).with("/my/file") @device.parse_options end it "should store the waitforcert options with --waitforcert" do @device.options.expects(:[]=).with(:waitforcert,42) @device.handle_waitforcert("42") end it "should set args[:Port] with --port" do @device.handle_port("42") @device.args[:Port].should == "42" end end describe "during setup" do before :each do @device.options.stubs(:[]) Puppet.stubs(:info) FileTest.stubs(:exists?).returns(true) Puppet[:libdir] = "/dev/null/lib" Puppet::SSL::Host.stubs(:ca_location=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) @host = stub_everything 'host' Puppet::SSL::Host.stubs(:new).returns(@host) Puppet.stubs(:settraps) end it "should call setup_logs" do @device.expects(:setup_logs) @device.setup end describe "when setting up logs" do before :each do Puppet::Util::Log.stubs(:newdestination) end it "should set log level to debug if --debug was passed" do @device.options.stubs(:[]).with(:debug).returns(true) @device.setup_logs Puppet::Util::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @device.options.stubs(:[]).with(:verbose).returns(true) @device.setup_logs Puppet::Util::Log.level.should == :info end [:verbose, :debug].each do |level| it "should set console as the log destination with level #{level}" do @device.options.stubs(:[]).with(level).returns(true) Puppet::Util::Log.expects(:newdestination).with(:console) @device.setup_logs end end it "should set syslog as the log destination if no --logdest" do @device.options.stubs(:[]).with(:setdest).returns(false) Puppet::Util::Log.expects(:newdestination).with(:syslog) @device.setup_logs end end it "should set a central log destination with --centrallogs" do @device.options.stubs(:[]).with(:centrallogs).returns(true) Puppet[:server] = "puppet.reductivelabs.com" Puppet::Util::Log.stubs(:newdestination).with(:syslog) Puppet::Util::Log.expects(:newdestination).with("puppet.reductivelabs.com") @device.setup end it "should use :main, :agent, :device and :ssl config" do Puppet.settings.expects(:use).with(:main, :agent, :device, :ssl) @device.setup end it "should install a remote ca location" do Puppet::SSL::Host.expects(:ca_location=).with(:remote) @device.setup end it "should tell the report handler to use REST" do Puppet::Transaction::Report.indirection.expects(:terminus_class=).with(:rest) @device.setup end it "should change the catalog_terminus setting to 'rest'" do Puppet[:catalog_terminus] = :foo @device.setup Puppet[:catalog_terminus].should == :rest end it "should tell the catalog handler to use cache" do Puppet::Resource::Catalog.indirection.expects(:cache_class=).with(:yaml) @device.setup end it "should change the facts_terminus setting to 'network_device'" do Puppet[:facts_terminus] = :foo @device.setup Puppet[:facts_terminus].should == :network_device end end describe "when initializing each devices SSL" do before(:each) do @host = stub_everything 'host' Puppet::SSL::Host.stubs(:new).returns(@host) end it "should create a new ssl host" do Puppet::SSL::Host.expects(:new).returns(@host) @device.setup_host end it "should wait for a certificate" do @device.options.stubs(:[]).with(:waitforcert).returns(123) @host.expects(:wait_for_cert).with(123) @device.setup_host end end describe "when running" do before :each do @device.options.stubs(:[]).with(:fingerprint).returns(false) Puppet.stubs(:notice) @device.options.stubs(:[]).with(:client) Puppet::Util::NetworkDevice::Config.stubs(:devices).returns({}) end it "should dispatch to main" do @device.stubs(:main) @device.run_command end it "should get the device list" do device_hash = stub_everything 'device hash' Puppet::Util::NetworkDevice::Config.expects(:devices).returns(device_hash) @device.main end it "should exit if the device list is empty" do expect { @device.main }.to exit_with 1 end describe "for each device" do before(:each) do - Puppet[:vardir] = "/dummy" - Puppet[:confdir] = "/dummy" + Puppet[:vardir] = make_absolute("/dummy") + Puppet[:confdir] = make_absolute("/dummy") Puppet[:certname] = "certname" @device_hash = { "device1" => OpenStruct.new(:name => "device1", :url => "url", :provider => "cisco"), "device2" => OpenStruct.new(:name => "device2", :url => "url", :provider => "cisco"), } Puppet::Util::NetworkDevice::Config.stubs(:devices).returns(@device_hash) Puppet.settings.stubs(:set_value) Puppet.settings.stubs(:use) @device.stubs(:setup_host) Puppet::Util::NetworkDevice.stubs(:init) @configurer = stub_everything 'configurer' Puppet::Configurer.stubs(:new).returns(@configurer) end it "should set vardir to the device vardir" do - Puppet.settings.expects(:set_value).with(:vardir, "/dummy/devices/device1", :cli) + Puppet.settings.expects(:set_value).with(:vardir, make_absolute("/dummy/devices/device1"), :cli) @device.main end it "should set confdir to the device confdir" do - Puppet.settings.expects(:set_value).with(:confdir, "/dummy/devices/device1", :cli) + Puppet.settings.expects(:set_value).with(:confdir, make_absolute("/dummy/devices/device1"), :cli) @device.main end it "should set certname to the device certname" do Puppet.settings.expects(:set_value).with(:certname, "device1", :cli) Puppet.settings.expects(:set_value).with(:certname, "device2", :cli) @device.main end it "should make sure all the required folders and files are created" do Puppet.settings.expects(:use).with(:main, :agent, :ssl).twice @device.main end it "should initialize the device singleton" do Puppet::Util::NetworkDevice.expects(:init).with(@device_hash["device1"]).then.with(@device_hash["device2"]) @device.main end it "should setup the SSL context" do @device.expects(:setup_host).twice @device.main end it "should launch a configurer for this device" do @configurer.expects(:run).twice @device.main end [:vardir, :confdir].each do |setting| it "should cleanup the #{setting} setting after the run" do configurer = states('configurer').starts_as('notrun') - Puppet.settings.expects(:set_value).with(setting, "/dummy/devices/device1", :cli).when(configurer.is('notrun')) + Puppet.settings.expects(:set_value).with(setting, make_absolute("/dummy/devices/device1"), :cli).when(configurer.is('notrun')) @configurer.expects(:run).twice.then(configurer.is('run')) - Puppet.settings.expects(:set_value).with(setting, "/dummy", :cli).when(configurer.is('run')) + Puppet.settings.expects(:set_value).with(setting, make_absolute("/dummy"), :cli).when(configurer.is('run')) @device.main end end it "should cleanup the certname setting after the run" do configurer = states('configurer').starts_as('notrun') Puppet.settings.expects(:set_value).with(:certname, "device1", :cli).when(configurer.is('notrun')) @configurer.expects(:run).twice.then(configurer.is('run')) Puppet.settings.expects(:set_value).with(:certname, "certname", :cli).when(configurer.is('run')) @device.main end end end end diff --git a/spec/unit/application/inspect_spec.rb b/spec/unit/application/inspect_spec.rb index 571683f37..9f12c83ad 100755 --- a/spec/unit/application/inspect_spec.rb +++ b/spec/unit/application/inspect_spec.rb @@ -1,277 +1,278 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/application/inspect' require 'puppet/resource/catalog' require 'puppet/indirector/catalog/yaml' require 'puppet/indirector/report/rest' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::Application::Inspect do include PuppetSpec::Files before :each do @inspect = Puppet::Application[:inspect] end describe "during setup" do it "should print its configuration if asked" do Puppet[:configprint] = "all" Puppet.settings.expects(:print_configs).returns(true) expect { @inspect.setup }.to exit_with 0 end it "should fail if reporting is turned off" do Puppet[:report] = false lambda { @inspect.setup }.should raise_error(/report=true/) end end describe "when executing" do before :each do Puppet[:report] = true @inspect.options[:logset] = true Puppet::Transaction::Report::Rest.any_instance.stubs(:save) @inspect.setup end it "should retrieve the local catalog" do Puppet::Resource::Catalog::Yaml.any_instance.expects(:find).with {|request| request.key == Puppet[:certname] }.returns(Puppet::Resource::Catalog.new) @inspect.run_command end it "should save the report to REST" do Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(Puppet::Resource::Catalog.new) Puppet::Transaction::Report::Rest.any_instance.expects(:save).with {|request| request.instance.host == Puppet[:certname] } @inspect.run_command end it "should audit the specified properties" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") file.puts("file contents") file.close resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command properties = events.inject({}) do |property_values, event| property_values.merge(event.property => event.previous_value) end properties["ensure"].should == :file properties["content"].should == "{md5}#{Digest::MD5.hexdigest("file contents\n")}" properties.has_key?("target").should == false end it "should set audited to true for all events" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command events.each do |event| event.audited.should == true end end it "should not report irrelevent attributes if the resource is absent" do catalog = Puppet::Resource::Catalog.new file = Tempfile.new("foo") resource = Puppet::Resource.new(:file, file.path, :parameters => {:audit => "all"}) + file.close file.delete catalog.add_resource(resource) Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(catalog) events = nil Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| events = request.instance.resource_statuses.values.first.events end @inspect.run_command properties = events.inject({}) do |property_values, event| property_values.merge(event.property => event.previous_value) end properties.should == {"ensure" => :absent} end describe "when archiving to a bucket" do before :each do Puppet[:archive_files] = true Puppet[:archive_file_server] = "filebucketserver" @catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(@catalog) end describe "when auditing files" do before :each do @file = tmpfile("foo") @resource = Puppet::Resource.new(:file, @file, :parameters => {:audit => "content"}) @catalog.add_resource(@resource) end it "should send an existing file to the file bucket" do File.open(@file, 'w') { |f| f.write('stuff') } Puppet::FileBucketFile::Rest.any_instance.expects(:head).with do |request| request.server == Puppet[:archive_file_server] end.returns(false) Puppet::FileBucketFile::Rest.any_instance.expects(:save).with do |request| request.server == Puppet[:archive_file_server] and request.instance.contents == 'stuff' end @inspect.run_command end - it "should not send unreadable files" do + it "should not send unreadable files", :fails_on_windows => true do File.open(@file, 'w') { |f| f.write('stuff') } File.chmod(0, @file) Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should not try to send non-existent files" do Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should not try to send files whose content we are not auditing" do @resource[:audit] = "group" Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end it "should continue if bucketing a file fails" do File.open(@file, 'w') { |f| f.write('stuff') } Puppet::FileBucketFile::Rest.any_instance.stubs(:head).returns false Puppet::FileBucketFile::Rest.any_instance.stubs(:save).raises "failure" Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| @report = request.instance end @inspect.run_command @report.logs.first.should_not == nil @report.logs.first.message.should =~ /Could not back up/ end end describe "when auditing non-files" do before :each do Puppet::Type.newtype(:stub_type) do newparam(:name) do desc "The name var" isnamevar end newproperty(:content) do desc "content" def retrieve :whatever end end end @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) end after :each do Puppet::Type.rmtype(:stub_type) end it "should not try to send non-files" do Puppet::FileBucketFile::Rest.any_instance.expects(:head).never Puppet::FileBucketFile::Rest.any_instance.expects(:save).never @inspect.run_command end end end describe "when there are failures" do before :each do Puppet::Type.newtype(:stub_type) do newparam(:name) do desc "The name var" isnamevar end newproperty(:content) do desc "content" def retrieve raise "failed" end end end @catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog::Yaml.any_instance.stubs(:find).returns(@catalog) Puppet::Transaction::Report::Rest.any_instance.expects(:save).with do |request| @report = request.instance end end after :each do Puppet::Type.rmtype(:stub_type) end it "should mark the report failed and create failed events for each property" do @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) @inspect.run_command @report.status.should == "failed" @report.logs.select{|log| log.message =~ /Could not inspect/}.size.should == 1 @report.resource_statuses.size.should == 1 @report.resource_statuses['Stub_type[foo]'].events.size.should == 1 event = @report.resource_statuses['Stub_type[foo]'].events.first event.property.should == "content" event.status.should == "failure" event.audited.should == true event.instance_variables.should_not include("@previous_value") end it "should continue to the next resource" do @resource = Puppet::Resource.new(:stub_type, 'foo', :parameters => {:audit => "all"}) @other_resource = Puppet::Resource.new(:stub_type, 'bar', :parameters => {:audit => "all"}) @catalog.add_resource(@resource) @catalog.add_resource(@other_resource) @inspect.run_command @report.resource_statuses.size.should == 2 @report.resource_statuses.keys.should =~ ['Stub_type[foo]', 'Stub_type[bar]'] end end end after :all do Puppet::Resource::Catalog.indirection.reset_terminus_class Puppet::Transaction::Report.indirection.terminus_class = :processor end end diff --git a/spec/unit/application/master_spec.rb b/spec/unit/application/master_spec.rb index e36df8caa..636988fd0 100755 --- a/spec/unit/application/master_spec.rb +++ b/spec/unit/application/master_spec.rb @@ -1,400 +1,400 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/application/master' require 'puppet/daemon' require 'puppet/network/server' -describe Puppet::Application::Master do +describe Puppet::Application::Master, :fails_on_windows => true do before :each do @master = Puppet::Application[:master] @daemon = stub_everything 'daemon' Puppet::Daemon.stubs(:new).returns(@daemon) Puppet::Util::Log.stubs(:newdestination) Puppet::Node.indirection.stubs(:terminus_class=) Puppet::Node.indirection.stubs(:cache_class=) Puppet::Node::Facts.indirection.stubs(:terminus_class=) Puppet::Node::Facts.indirection.stubs(:cache_class=) Puppet::Transaction::Report.indirection.stubs(:terminus_class=) Puppet::Resource::Catalog.indirection.stubs(:terminus_class=) Puppet::SSL::Host.stubs(:ca_location=) end it "should operate in master run_mode" do @master.class.run_mode.name.should equal(:master) end it "should ask Puppet::Application to parse Puppet configuration file" do @master.should_parse_config?.should be_true end it "should declare a main command" do @master.should respond_to(:main) end it "should declare a compile command" do @master.should respond_to(:compile) end it "should declare a preinit block" do @master.should respond_to(:preinit) end describe "during preinit" do before :each do @master.stubs(:trap) end it "should catch INT" do @master.stubs(:trap).with { |arg,block| arg == :INT } @master.preinit end it "should create a Puppet Daemon" do Puppet::Daemon.expects(:new).returns(@daemon) @master.preinit end it "should give ARGV to the Daemon" do argv = stub 'argv' ARGV.stubs(:dup).returns(argv) @daemon.expects(:argv=).with(argv) @master.preinit end end [:debug,:verbose].each do |option| it "should declare handle_#{option} method" do @master.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @master.options.expects(:[]=).with(option, 'arg') @master.send("handle_#{option}".to_sym, 'arg') end end describe "when applying options" do before do @master.command_line.stubs(:args).returns([]) end it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @master.handle_logdest("console") end it "should put the setdest options to true" do @master.options.expects(:[]=).with(:setdest,true) @master.handle_logdest("console") end it "should parse the log destination from ARGV" do @master.command_line.stubs(:args).returns(%w{--logdest /my/file}) Puppet::Util::Log.expects(:newdestination).with("/my/file") @master.parse_options end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::SSL::CertificateAuthority.stubs(:instance) Puppet::SSL::CertificateAuthority.stubs(:ca?) Puppet.settings.stubs(:use) @master.options.stubs(:[]).with(any_parameters) end it "should abort stating that the master is not supported on Windows" do Puppet.features.stubs(:microsoft_windows?).returns(true) expect { @master.setup }.to raise_error(Puppet::Error, /Puppet master is not supported on Microsoft Windows/) end it "should set log level to debug if --debug was passed" do @master.options.stubs(:[]).with(:debug).returns(true) @master.setup Puppet::Log.level.should == :debug end it "should set log level to info if --verbose was passed" do @master.options.stubs(:[]).with(:verbose).returns(true) @master.setup Puppet::Log.level.should == :info end it "should set console as the log destination if no --logdest and --daemonize" do @master.stubs(:[]).with(:daemonize).returns(:false) Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should set syslog as the log destination if no --logdest and not --daemonize" do Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should set syslog as the log destination if --rack" do @master.options.stubs(:[]).with(:rack).returns(:true) Puppet::Log.expects(:newdestination).with(:syslog) @master.setup end it "should print puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs).returns(true) expect { @master.setup }.to exit_with 0 end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) expect { @master.setup }.to exit_with 1 end it "should tell Puppet.settings to use :main,:ssl,:master and :metrics category" do Puppet.settings.expects(:use).with(:main,:master,:ssl,:metrics) @master.setup end it "should cache class in yaml" do Puppet::Node.indirection.expects(:cache_class=).with(:yaml) @master.setup end describe "with no ca" do it "should set the ca_location to none" do Puppet::SSL::Host.expects(:ca_location=).with(:none) @master.setup end end describe "with a ca configured" do before :each do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) end it "should set the ca_location to local" do Puppet::SSL::Host.expects(:ca_location=).with(:local) @master.setup end it "should tell Puppet.settings to use :ca category" do Puppet.settings.expects(:use).with(:ca) @master.setup end it "should instantiate the CertificateAuthority singleton" do Puppet::SSL::CertificateAuthority.expects(:instance) @master.setup end end end describe "when running" do before do @master.preinit end it "should dispatch to compile if called with --compile" do @master.options[:node] = "foo" @master.expects(:compile) @master.run_command end it "should dispatch to main otherwise" do @master.options[:node] = nil @master.expects(:main) @master.run_command end describe "the compile command" do before do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") Puppet.stubs(:err) @master.stubs(:jj) Puppet.features.stubs(:pson?).returns true end it "should fail if pson isn't available" do Puppet.features.expects(:pson?).returns false lambda { @master.compile }.should raise_error end it "should compile a catalog for the specified node" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).with("foo").returns Puppet::Resource::Catalog.new $stdout.stubs(:puts) expect { @master.compile }.to exit_with 0 end it "should convert the catalog to a pure-resource catalog and use 'jj' to pretty-print the catalog" do catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.indirection.expects(:find).returns catalog catalog.expects(:to_resource).returns("rescat") @master.options[:node] = "foo" @master.expects(:jj).with("rescat") expect { @master.compile }.to exit_with 0 end it "should exit with error code 30 if no catalog can be found" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).returns nil $stderr.expects(:puts) expect { @master.compile }.to exit_with 30 end it "should exit with error code 30 if there's a failure" do @master.options[:node] = "foo" Puppet::Resource::Catalog.indirection.expects(:find).raises ArgumentError $stderr.expects(:puts) expect { @master.compile }.to exit_with 30 end end describe "the main command" do before :each do @master.preinit @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) @app = stub_everything 'app' Puppet::SSL::Host.stubs(:localhost) Puppet::SSL::CertificateAuthority.stubs(:ca?) Process.stubs(:uid).returns(1000) Puppet.stubs(:service) Puppet.stubs(:[]) Puppet.stubs(:notice) Puppet.stubs(:start) end it "should create a Server" do Puppet::Network::Server.expects(:new) @master.main end it "should give the server to the daemon" do @daemon.expects(:server=).with(@server) @master.main end it "should create the server with the right XMLRPC handlers" do Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket]} @master.main end it "should create the server with a :ca xmlrpc handler if needed" do Puppet.stubs(:[]).with(:ca).returns(true) Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers].include?(:CA) } @master.main end it "should generate a SSL cert for localhost" do Puppet::SSL::Host.expects(:localhost) @master.main end it "should make sure to *only* hit the CA for data" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:only) @master.main end it "should drop privileges if running as root" do Puppet.features.stubs(:root?).returns true Puppet::Util.expects(:chuser) @master.main end it "should daemonize if needed" do Puppet.stubs(:[]).with(:daemonize).returns(true) @daemon.expects(:daemonize) @master.main end it "should start the service" do @daemon.expects(:start) @master.main end describe "with --rack", :if => Puppet.features.rack? do before do require 'puppet/network/http/rack' Puppet::Network::HTTP::Rack.stubs(:new).returns(@app) end it "it should create the app with REST and XMLRPC support" do @master.options.stubs(:[]).with(:rack).returns(:true) Puppet::Network::HTTP::Rack.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket] and args[:protocols] == [:rest, :xmlrpc] } @master.main end it "it should not start a daemon" do @master.options.stubs(:[]).with(:rack).returns(:true) @daemon.expects(:start).never @master.main end it "it should return the app" do @master.options.stubs(:[]).with(:rack).returns(:true) app = @master.main app.should equal(@app) end end end end end diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb index 17b285d53..8bb6a3dc6 100755 --- a/spec/unit/configurer/downloader_spec.rb +++ b/spec/unit/configurer/downloader_spec.rb @@ -1,199 +1,200 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/configurer/downloader' describe Puppet::Configurer::Downloader do require 'puppet_spec/files' include PuppetSpec::Files it "should require a name" do lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) end it "should require a path and a source at initialization" do lambda { Puppet::Configurer::Downloader.new("name") }.should raise_error(ArgumentError) end it "should set the name, path and source appropriately" do dler = Puppet::Configurer::Downloader.new("facts", "path", "source") dler.name.should == "facts" dler.path.should == "path" dler.source.should == "source" end it "should be able to provide a timeout value" do Puppet::Configurer::Downloader.should respond_to(:timeout) end it "should use the configtimeout, converted to an integer, as its timeout" do Puppet.settings.expects(:value).with(:configtimeout).returns "50" Puppet::Configurer::Downloader.timeout.should == 50 end describe "when creating the file that does the downloading" do before do @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") end it "should create a file instance with the right path and source" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:path] == "path" and opts[:source] == "source" } @dler.file end it "should tag the file with the downloader name" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:tag] == "foo" } @dler.file end it "should always recurse" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:recurse] == true } @dler.file end it "should always purge" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:purge] == true } @dler.file end it "should never be in noop" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:noop] == false } @dler.file end it "should always set the owner to the current UID" do Process.expects(:uid).returns 51 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == 51 } @dler.file end it "should always set the group to the current GID" do Process.expects(:gid).returns 61 Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == 61 } @dler.file end it "should always force the download" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:force] == true } @dler.file end it "should never back up when downloading" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:backup] == false } @dler.file end it "should support providing an 'ignore' parameter" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == [".svn"] } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn") @dler.file end it "should split the 'ignore' parameter on whitespace" do Puppet::Type.type(:file).expects(:new).with { |opts| opts[:ignore] == %w{.svn CVS} } @dler = Puppet::Configurer::Downloader.new("foo", "path", "source", ".svn CVS") @dler.file end end describe "when creating the catalog to do the downloading" do before do - @dler = Puppet::Configurer::Downloader.new("foo", "/download/path", "source") + @path = make_absolute("/download/path") + @dler = Puppet::Configurer::Downloader.new("foo", @path, "source") end it "should create a catalog and add the file to it" do catalog = @dler.catalog catalog.resources.size.should == 1 catalog.resources.first.class.should == Puppet::Type::File - catalog.resources.first.name.should == "/download/path" + catalog.resources.first.name.should == @path end it "should specify that it is not managing a host catalog" do @dler.catalog.host_config.should == false end end describe "when downloading" do before do @dl_name = tmpfile("downloadpath") source_name = tmpfile("source") File.open(source_name, 'w') {|f| f.write('hola mundo') } @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name) end - it "should not skip downloaded resources when filtering on tags" do + it "should not skip downloaded resources when filtering on tags", :fails_on_windows => true do Puppet[:tags] = 'maytag' @dler.evaluate File.exists?(@dl_name).should be_true end it "should log that it is downloading" do Puppet.expects(:info) Timeout.stubs(:timeout) @dler.evaluate end it "should set a timeout for the download" do Puppet::Configurer::Downloader.expects(:timeout).returns 50 Timeout.expects(:timeout).with(50) @dler.evaluate end it "should apply the catalog within the timeout block" do catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) Timeout.expects(:timeout).yields catalog.expects(:apply) @dler.evaluate end it "should return all changed file paths" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) @dler.evaluate.should == %w{/changed/file} end it "should yield the resources if a block is given" do trans = mock 'transaction' catalog = mock 'catalog' @dler.expects(:catalog).returns(catalog) catalog.expects(:apply).yields(trans) Timeout.expects(:timeout).yields resource = mock 'resource' resource.expects(:[]).with(:path).returns "/changed/file" trans.expects(:changed?).returns([resource]) yielded = nil @dler.evaluate { |r| yielded = r } yielded.should == resource end it "should catch and log exceptions" do Puppet.expects(:err) Timeout.stubs(:timeout).raises(Puppet::Error, "testing") lambda { @dler.evaluate }.should_not raise_error end end end diff --git a/spec/unit/daemon_spec.rb b/spec/unit/daemon_spec.rb index e2679a966..fc43d93ad 100755 --- a/spec/unit/daemon_spec.rb +++ b/spec/unit/daemon_spec.rb @@ -1,289 +1,291 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/daemon' def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Daemon do before do @daemon = Puppet::Daemon.new end it "should be able to manage an agent" do @daemon.should respond_to(:agent) end it "should be able to manage a network server" do @daemon.should respond_to(:server) end it "should reopen the Log logs when told to reopen logs" do Puppet::Util::Log.expects(:reopen) @daemon.reopen_logs end describe "when setting signal traps" do - {:INT => :stop, :TERM => :stop, :HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}.each do |signal, method| + signals = {:INT => :stop, :TERM => :stop } + signals.update({:HUP => :restart, :USR1 => :reload, :USR2 => :reopen_logs}) unless Puppet.features.microsoft_windows? + signals.each do |signal, method| it "should log and call #{method} when it receives #{signal}" do Signal.expects(:trap).with(signal).yields Puppet.expects(:notice) @daemon.expects(method) @daemon.set_signal_traps end end end describe "when starting" do before do @daemon.stubs(:create_pidfile) @daemon.stubs(:set_signal_traps) EventLoop.current.stubs(:run) end it "should fail if it has neither agent nor server" do lambda { @daemon.start }.should raise_error(Puppet::DevError) end it "should create its pidfile" do @daemon.stubs(:agent).returns stub('agent', :start => nil) @daemon.expects(:create_pidfile) @daemon.start end it "should start the agent if the agent is configured" do agent = mock 'agent' agent.expects(:start) @daemon.stubs(:agent).returns agent @daemon.start end it "should start its server if one is configured" do server = mock 'server' server.expects(:start) @daemon.stubs(:server).returns server @daemon.start end it "should let the current EventLoop run" do @daemon.stubs(:agent).returns stub('agent', :start => nil) EventLoop.current.expects(:run) @daemon.start end end describe "when stopping" do before do @daemon.stubs(:remove_pidfile) Puppet::Util::Log.stubs(:close_all) # to make the global safe to mock, set it to a subclass of itself, # then restore it in an after pass without_warnings { Puppet::Application = Class.new(Puppet::Application) } end after do # restore from the superclass so we lose the stub garbage without_warnings { Puppet::Application = Puppet::Application.superclass } end it "should stop its server if one is configured" do server = mock 'server' server.expects(:stop) @daemon.stubs(:server).returns server expect { @daemon.stop }.to exit_with 0 end it 'should request a stop from Puppet::Application' do Puppet::Application.expects(:stop!) expect { @daemon.stop }.to exit_with 0 end it "should remove its pidfile" do @daemon.expects(:remove_pidfile) expect { @daemon.stop }.to exit_with 0 end it "should close all logs" do Puppet::Util::Log.expects(:close_all) expect { @daemon.stop }.to exit_with 0 end it "should exit unless called with ':exit => false'" do expect { @daemon.stop }.to exit_with 0 end it "should not exit if called with ':exit => false'" do @daemon.stop :exit => false end end describe "when creating its pidfile" do it "should use an exclusive mutex" do Puppet.settings.expects(:value).with(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @daemon.create_pidfile end it "should lock the pidfile using the Pidlock class" do pidfile = mock 'pidfile' Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.expects(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns true @daemon.create_pidfile end it "should fail if it cannot lock" do pidfile = mock 'pidfile' Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:lock).returns false lambda { @daemon.create_pidfile }.should raise_error end end describe "when removing its pidfile" do it "should use an exclusive mutex" do Puppet.settings.expects(:value).with(:name).returns "me" Puppet::Util.expects(:synchronize_on).with("me",Sync::EX) @daemon.remove_pidfile end it "should do nothing if the pidfile is not present" do pidfile = mock 'pidfile', :locked? => false Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" pidfile.expects(:unlock).never @daemon.remove_pidfile end it "should unlock the pidfile using the Pidlock class" do pidfile = mock 'pidfile', :locked? => true Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:unlock).returns true Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" @daemon.remove_pidfile end it "should warn if it cannot remove the pidfile" do pidfile = mock 'pidfile', :locked? => true Puppet::Util::Pidlock.expects(:new).with("/my/file").returns pidfile pidfile.expects(:unlock).returns false Puppet.settings.stubs(:value).with(:name).returns "eh" Puppet.settings.stubs(:value).with(:pidfile).returns "/my/file" Puppet.expects :err @daemon.remove_pidfile end end describe "when reloading" do it "should do nothing if no agent is configured" do @daemon.reload end it "should do nothing if the agent is running" do agent = mock 'agent' agent.expects(:running?).returns true @daemon.stubs(:agent).returns agent @daemon.reload end it "should run the agent if one is available and it is not running" do agent = mock 'agent' agent.expects(:running?).returns false agent.expects :run @daemon.stubs(:agent).returns agent @daemon.reload end end describe "when restarting" do before do without_warnings { Puppet::Application = Class.new(Puppet::Application) } end after do without_warnings { Puppet::Application = Puppet::Application.superclass } end it 'should set Puppet::Application.restart!' do Puppet::Application.expects(:restart!) @daemon.stubs(:reexec) @daemon.restart end it "should reexec itself if no agent is available" do @daemon.expects(:reexec) @daemon.restart end it "should reexec itself if the agent is not running" do agent = mock 'agent' agent.expects(:running?).returns false @daemon.stubs(:agent).returns agent @daemon.expects(:reexec) @daemon.restart end end describe "when reexecing it self" do before do @daemon.stubs(:exec) @daemon.stubs(:stop) end it "should fail if no argv values are available" do @daemon.expects(:argv).returns nil lambda { @daemon.reexec }.should raise_error(Puppet::DevError) end it "should shut down without exiting" do @daemon.argv = %w{foo} @daemon.expects(:stop).with(:exit => false) @daemon.reexec end it "should call 'exec' with the original executable and arguments" do @daemon.argv = %w{foo} @daemon.expects(:exec).with($0 + " foo") @daemon.reexec end end end diff --git a/spec/unit/file_bucket/dipper_spec.rb b/spec/unit/file_bucket/dipper_spec.rb index 910b2808d..431b12371 100755 --- a/spec/unit/file_bucket/dipper_spec.rb +++ b/spec/unit/file_bucket/dipper_spec.rb @@ -1,113 +1,113 @@ #!/usr/bin/env rspec require 'spec_helper' require 'pathname' require 'puppet/file_bucket/dipper' require 'puppet/indirector/file_bucket_file/rest' describe Puppet::FileBucket::Dipper do include PuppetSpec::Files def make_tmp_file(contents) file = tmpfile("file_bucket_file") File.open(file, 'w') { |f| f.write(contents) } file end it "should fail in an informative way when there are failures checking for the file on the server" do - @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") + @dipper = Puppet::FileBucket::Dipper.new(:Path => make_absolute("/my/bucket")) file = make_tmp_file('contents') Puppet::FileBucket::File.indirection.expects(:head).raises ArgumentError lambda { @dipper.backup(file) }.should raise_error(Puppet::Error) end it "should fail in an informative way when there are failures backing up to the server" do - @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") + @dipper = Puppet::FileBucket::Dipper.new(:Path => make_absolute("/my/bucket")) file = make_tmp_file('contents') Puppet::FileBucket::File.indirection.expects(:head).returns false Puppet::FileBucket::File.indirection.expects(:save).raises ArgumentError lambda { @dipper.backup(file) }.should raise_error(Puppet::Error) end - it "should backup files to a local bucket" do + it "should backup files to a local bucket", :fails_on_windows => true do Puppet[:bucketdir] = "/non/existent/directory" file_bucket = tmpdir("bucket") @dipper = Puppet::FileBucket::Dipper.new(:Path => file_bucket) file = make_tmp_file('my contents') checksum = "2975f560750e71c478b8e3b39a956adb" Digest::MD5.hexdigest('my contents').should == checksum @dipper.backup(file).should == checksum File.exists?("#{file_bucket}/2/9/7/5/f/5/6/0/2975f560750e71c478b8e3b39a956adb/contents").should == true end it "should not backup a file that is already in the bucket" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") file = make_tmp_file('my contents') checksum = Digest::MD5.hexdigest('my contents') Puppet::FileBucket::File.indirection.expects(:head).returns true Puppet::FileBucket::File.indirection.expects(:save).never @dipper.backup(file).should == checksum end it "should retrieve files from a local bucket" do @dipper = Puppet::FileBucket::Dipper.new(:Path => "/my/bucket") checksum = Digest::MD5.hexdigest('my contents') request = nil Puppet::FileBucketFile::File.any_instance.expects(:find).with{ |r| request = r }.once.returns(Puppet::FileBucket::File.new('my contents')) @dipper.getfile(checksum).should == 'my contents' request.key.should == "md5/#{checksum}" end it "should backup files to a remote server" do @dipper = Puppet::FileBucket::Dipper.new(:Server => "puppetmaster", :Port => "31337") file = make_tmp_file('my contents') checksum = Digest::MD5.hexdigest('my contents') real_path = Pathname.new(file).realpath request1 = nil request2 = nil Puppet::FileBucketFile::Rest.any_instance.expects(:head).with { |r| request1 = r }.once.returns(nil) Puppet::FileBucketFile::Rest.any_instance.expects(:save).with { |r| request2 = r }.once @dipper.backup(file).should == checksum [request1, request2].each do |r| r.server.should == 'puppetmaster' r.port.should == 31337 r.key.should == "md5/#{checksum}#{real_path}" end end it "should retrieve files from a remote server" do @dipper = Puppet::FileBucket::Dipper.new(:Server => "puppetmaster", :Port => "31337") checksum = Digest::MD5.hexdigest('my contents') request = nil Puppet::FileBucketFile::Rest.any_instance.expects(:find).with { |r| request = r }.returns(Puppet::FileBucket::File.new('my contents')) @dipper.getfile(checksum).should == "my contents" request.server.should == 'puppetmaster' request.port.should == 31337 request.key.should == "md5/#{checksum}" end end diff --git a/spec/unit/file_serving/configuration_spec.rb b/spec/unit/file_serving/configuration_spec.rb index 6ee1a4f38..ed8663853 100755 --- a/spec/unit/file_serving/configuration_spec.rb +++ b/spec/unit/file_serving/configuration_spec.rb @@ -1,248 +1,249 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/file_serving/configuration' -describe Puppet::FileServing::Configuration do +describe Puppet::FileServing::Configuration, :fails_on_windows => true do it "should make :new a private method" do proc { Puppet::FileServing::Configuration.new }.should raise_error end it "should return the same configuration each time :create is called" do Puppet::FileServing::Configuration.create.should equal(Puppet::FileServing::Configuration.create) end it "should have a method for removing the current configuration instance" do old = Puppet::FileServing::Configuration.create Puppet::Util::Cacher.expire Puppet::FileServing::Configuration.create.should_not equal(old) end after do Puppet::Util::Cacher.expire end end describe Puppet::FileServing::Configuration do + include PuppetSpec::Files before :each do - @path = "/path/to/configuration/file.conf" + @path = make_absolute("/path/to/configuration/file.conf") Puppet.settings.stubs(:value).with(:trace).returns(false) Puppet.settings.stubs(:value).with(:fileserverconfig).returns(@path) end after :each do Puppet::Util::Cacher.expire end describe "when initializing" do it "should work without a configuration file" do FileTest.stubs(:exists?).with(@path).returns(false) proc { Puppet::FileServing::Configuration.create }.should_not raise_error end it "should parse the configuration file if present" do FileTest.stubs(:exists?).with(@path).returns(true) @parser = mock 'parser' @parser.expects(:parse).returns({}) Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) Puppet::FileServing::Configuration.create end it "should determine the path to the configuration file from the Puppet settings" do Puppet::FileServing::Configuration.create end end describe "when parsing the configuration file" do before do FileTest.stubs(:exists?).with(@path).returns(true) @parser = mock 'parser' Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser) end it "should set the mount list to the results of parsing" do @parser.expects(:parse).returns("one" => mock("mount")) config = Puppet::FileServing::Configuration.create config.mounted?("one").should be_true end it "should not raise exceptions" do @parser.expects(:parse).raises(ArgumentError) proc { Puppet::FileServing::Configuration.create }.should_not raise_error end it "should replace the existing mount list with the results of reparsing" do @parser.expects(:parse).returns("one" => mock("mount")) config = Puppet::FileServing::Configuration.create config.mounted?("one").should be_true # Now parse again @parser.expects(:parse).returns("two" => mock('other')) config.send(:readconfig, false) config.mounted?("one").should be_false config.mounted?("two").should be_true end it "should not replace the mount list until the file is entirely parsed successfully" do @parser.expects(:parse).returns("one" => mock("mount")) @parser.expects(:parse).raises(ArgumentError) config = Puppet::FileServing::Configuration.create # Now parse again, so the exception gets thrown config.send(:readconfig, false) config.mounted?("one").should be_true end it "should add modules and plugins mounts even if the file does not exist" do FileTest.expects(:exists?).returns false # the file doesn't exist config = Puppet::FileServing::Configuration.create config.mounted?("modules").should be_true config.mounted?("plugins").should be_true end it "should allow all access to modules and plugins if no fileserver.conf exists" do FileTest.expects(:exists?).returns false # the file doesn't exist modules = stub 'modules', :empty? => true Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) modules.expects(:allow).with('*') plugins = stub 'plugins', :empty? => true Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) plugins.expects(:allow).with('*') Puppet::FileServing::Configuration.create end it "should not allow access from all to modules and plugins if the fileserver.conf provided some rules" do FileTest.expects(:exists?).returns false # the file doesn't exist modules = stub 'modules', :empty? => false Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules) modules.expects(:allow).with('*').never plugins = stub 'plugins', :empty? => false Puppet::FileServing::Mount::Plugins.stubs(:new).returns(plugins) plugins.expects(:allow).with('*').never Puppet::FileServing::Configuration.create end it "should add modules and plugins mounts even if they are not returned by the parser" do @parser.expects(:parse).returns("one" => mock("mount")) FileTest.expects(:exists?).returns true # the file doesn't exist config = Puppet::FileServing::Configuration.create config.mounted?("modules").should be_true config.mounted?("plugins").should be_true end end describe "when finding the specified mount" do it "should choose the named mount if one exists" do config = Puppet::FileServing::Configuration.create config.expects(:mounts).returns("one" => "foo") config.find_mount("one", mock('env')).should == "foo" end it "should use the provided environment to find a matching module if the named module cannot be found" do config = Puppet::FileServing::Configuration.create mod = mock 'module' env = mock 'environment' env.expects(:module).with("foo").returns mod mount = mock 'mount' config.stubs(:mounts).returns("modules" => mount) Puppet::Util::Warnings.expects(:notice_once) config.find_mount("foo", env).should equal(mount) end it "should return nil if there is no such named mount and no module with the same name exists" do config = Puppet::FileServing::Configuration.create env = mock 'environment' env.expects(:module).with("foo").returns nil mount = mock 'mount' config.stubs(:mounts).returns("modules" => mount) config.find_mount("foo", env).should be_nil end end describe "when finding the mount name and relative path in a request key" do before do @config = Puppet::FileServing::Configuration.create @config.stubs(:find_mount) @request = stub 'request', :key => "foo/bar/baz", :options => {}, :node => nil, :environment => mock("env") end it "should reread the configuration" do @config.expects(:readconfig) @config.split_path(@request) end it "should treat the first field of the URI path as the mount name" do @config.expects(:find_mount).with { |name, node| name == "foo" } @config.split_path(@request) end it "should fail if the mount name is not alpha-numeric" do @request.expects(:key).returns "foo&bar/asdf" lambda { @config.split_path(@request) }.should raise_error(ArgumentError) end it "should support dashes in the mount name" do @request.expects(:key).returns "foo-bar/asdf" lambda { @config.split_path(@request) }.should_not raise_error(ArgumentError) end it "should use the mount name and environment to find the mount" do @config.expects(:find_mount).with { |name, env| name == "foo" and env == @request.environment } @request.stubs(:node).returns("mynode") @config.split_path(@request) end it "should return nil if the mount cannot be found" do @config.expects(:find_mount).returns nil @config.split_path(@request).should be_nil end it "should return the mount and the relative path if the mount is found" do mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "bar/baz"] end it "should remove any double slashes" do @request.stubs(:key).returns "foo/bar//baz" mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "bar/baz"] end it "should return the relative path as nil if it is an empty string" do @request.expects(:key).returns "foo" mount = stub 'mount', :name => "foo" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, nil] end it "should add 'modules/' to the relative path if the modules mount is used but not specified, for backward compatibility" do @request.expects(:key).returns "foo/bar" mount = stub 'mount', :name => "modules" @config.expects(:find_mount).returns mount @config.split_path(@request).should == [mount, "foo/bar"] end end end diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb index 41810650a..aff4c91fa 100755 --- a/spec/unit/file_serving/fileset_spec.rb +++ b/spec/unit/file_serving/fileset_spec.rb @@ -1,362 +1,378 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/file_serving/fileset' describe Puppet::FileServing::Fileset, " when initializing" do + include PuppetSpec::Files + + before :each do + @somefile = make_absolute("/some/file") + end + it "should require a path" do proc { Puppet::FileServing::Fileset.new }.should raise_error(ArgumentError) end it "should fail if its path is not fully qualified" do proc { Puppet::FileServing::Fileset.new("some/file") }.should raise_error(ArgumentError) end it "should not fail if the path is fully qualified, with a trailing separator" do - path = "/some/path/with/trailing/separator" - path_with_separator = "#{path}#{File::SEPARATOR}" - File.stubs(:lstat).with(path).returns stub('stat') + path_with_separator = "#{@somefile}#{File::SEPARATOR}" + File.stubs(:lstat).with(@somefile).returns stub('stat') fileset = Puppet::FileServing::Fileset.new(path_with_separator) - fileset.path.should == path + fileset.path.should == @somefile end it "should not fail if the path is just the file separator" do - path = File::SEPARATOR + path = make_absolute(File::SEPARATOR) File.stubs(:lstat).with(path).returns stub('stat') fileset = Puppet::FileServing::Fileset.new(path) fileset.path.should == path end it "should fail if its path does not exist" do - File.expects(:lstat).with("/some/file").returns nil - proc { Puppet::FileServing::Fileset.new("/some/file") }.should raise_error(ArgumentError) + File.expects(:lstat).with(@somefile).returns nil + proc { Puppet::FileServing::Fileset.new(@somefile) }.should raise_error(ArgumentError) end it "should accept a 'recurse' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :recurse => true) + File.expects(:lstat).with(@somefile).returns stub("stat") + set = Puppet::FileServing::Fileset.new(@somefile, :recurse => true) set.recurse.should be_true end it "should accept a 'recurselimit' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :recurselimit => 3) + File.expects(:lstat).with(@somefile).returns stub("stat") + set = Puppet::FileServing::Fileset.new(@somefile, :recurselimit => 3) set.recurselimit.should == 3 end it "should accept an 'ignore' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :ignore => ".svn") + File.expects(:lstat).with(@somefile).returns stub("stat") + set = Puppet::FileServing::Fileset.new(@somefile, :ignore => ".svn") set.ignore.should == [".svn"] end it "should accept a 'links' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :links => :manage) + File.expects(:lstat).with(@somefile).returns stub("stat") + set = Puppet::FileServing::Fileset.new(@somefile, :links => :manage) set.links.should == :manage end it "should accept a 'checksum_type' option" do - File.expects(:lstat).with("/some/file").returns stub("stat") - set = Puppet::FileServing::Fileset.new("/some/file", :checksum_type => :test) + File.expects(:lstat).with(@somefile).returns stub("stat") + set = Puppet::FileServing::Fileset.new(@somefile, :checksum_type => :test) set.checksum_type.should == :test end it "should fail if 'links' is set to anything other than :manage or :follow" do - proc { Puppet::FileServing::Fileset.new("/some/file", :links => :whatever) }.should raise_error(ArgumentError) + proc { Puppet::FileServing::Fileset.new(@somefile, :links => :whatever) }.should raise_error(ArgumentError) end it "should default to 'false' for recurse" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").recurse.should == false + File.expects(:lstat).with(@somefile).returns stub("stat") + Puppet::FileServing::Fileset.new(@somefile).recurse.should == false end it "should default to :infinite for recurselimit" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").recurselimit.should == :infinite + File.expects(:lstat).with(@somefile).returns stub("stat") + Puppet::FileServing::Fileset.new(@somefile).recurselimit.should == :infinite end it "should default to an empty ignore list" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").ignore.should == [] + File.expects(:lstat).with(@somefile).returns stub("stat") + Puppet::FileServing::Fileset.new(@somefile).ignore.should == [] end it "should default to :manage for links" do - File.expects(:lstat).with("/some/file").returns stub("stat") - Puppet::FileServing::Fileset.new("/some/file").links.should == :manage + File.expects(:lstat).with(@somefile).returns stub("stat") + Puppet::FileServing::Fileset.new(@somefile).links.should == :manage end it "should support using an Indirector Request for its options" do - File.expects(:lstat).with("/some/file").returns stub("stat") + File.expects(:lstat).with(@somefile).returns stub("stat") request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") - lambda { Puppet::FileServing::Fileset.new("/some/file", request) }.should_not raise_error + lambda { Puppet::FileServing::Fileset.new(@somefile, request) }.should_not raise_error end describe "using an indirector request" do before do File.stubs(:lstat).returns stub("stat") @values = {:links => :manage, :ignore => %w{a b}, :recurse => true, :recurselimit => 1234} @request = Puppet::Indirector::Request.new(:file_serving, :find, "foo") + @myfile = make_absolute("/my/file") end [:recurse, :recurselimit, :ignore, :links].each do |option| it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present" do @request.stubs(:options).returns(option => @values[option]) - Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] + Puppet::FileServing::Fileset.new(@myfile, @request).send(option).should == @values[option] end it "should pass :recurse, :recurselimit, :ignore, and :links settings on to the fileset if present with the keys stored as strings" do @request.stubs(:options).returns(option.to_s => @values[option]) - Puppet::FileServing::Fileset.new("/my/file", @request).send(option).should == @values[option] + Puppet::FileServing::Fileset.new(@myfile, @request).send(option).should == @values[option] end end it "should convert the integer as a string to their integer counterpart when setting options" do @request.stubs(:options).returns(:recurselimit => "1234") - Puppet::FileServing::Fileset.new("/my/file", @request).recurselimit.should == 1234 + Puppet::FileServing::Fileset.new(@myfile, @request).recurselimit.should == 1234 end it "should convert the string 'true' to the boolean true when setting options" do @request.stubs(:options).returns(:recurse => "true") - Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == true + Puppet::FileServing::Fileset.new(@myfile, @request).recurse.should == true end it "should convert the string 'false' to the boolean false when setting options" do @request.stubs(:options).returns(:recurse => "false") - Puppet::FileServing::Fileset.new("/my/file", @request).recurse.should == false + Puppet::FileServing::Fileset.new(@myfile, @request).recurse.should == false end end end describe Puppet::FileServing::Fileset, " when determining whether to recurse" do + include PuppetSpec::Files + before do - @path = "/my/path" + @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat") @fileset = Puppet::FileServing::Fileset.new(@path) end it "should always recurse if :recurse is set to 'true' and with infinite recursion" do @fileset.recurse = true @fileset.recurselimit = :infinite @fileset.recurse?(0).should be_true end it "should never recurse if :recurse is set to 'false'" do @fileset.recurse = false @fileset.recurse?(-1).should be_false end it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is less than that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(0).should be_true end it "should recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is equal to that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(1).should be_true end it "should not recurse if :recurse is set to true, :recurselimit is set to an integer and the current depth is greater than that integer" do @fileset.recurse = true @fileset.recurselimit = 1 @fileset.recurse?(2).should be_false end end describe Puppet::FileServing::Fileset, " when recursing" do + include PuppetSpec::Files + before do - @path = "/my/path" + @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) @dirstat = stub 'dirstat', :directory? => true @filestat = stub 'filestat', :directory? => false end def mock_dir_structure(path, stat_method = :lstat) File.stubs(stat_method).with(path).returns(@dirstat) Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS}) # Keep track of the files we're stubbing. @files = %w{.} %w{one two .svn CVS}.each do |subdir| @files << subdir # relative path subpath = File.join(path, subdir) File.stubs(stat_method).with(subpath).returns(@dirstat) Dir.stubs(:entries).with(subpath).returns(%w{.svn CVS file1 file2}) %w{file1 file2 .svn CVS}.each do |file| @files << File.join(subdir, file) # relative path File.stubs(stat_method).with(File.join(subpath, file)).returns(@filestat) end end end it "should recurse through the whole file tree if :recurse is set to 'true'" do mock_dir_structure(@path) @fileset.stubs(:recurse?).returns(true) @fileset.files.sort.should == @files.sort end it "should not recurse if :recurse is set to 'false'" do mock_dir_structure(@path) @fileset.stubs(:recurse?).returns(false) @fileset.files.should == %w{.} end # It seems like I should stub :recurse? here, or that I shouldn't stub the # examples above, but... it "should recurse to the level set if :recurselimit is set to an integer" do mock_dir_structure(@path) @fileset.recurse = true @fileset.recurselimit = 1 @fileset.files.should == %w{. one two .svn CVS} end it "should ignore the '.' and '..' directories in subdirectories" do mock_dir_structure(@path) @fileset.recurse = true @fileset.files.sort.should == @files.sort end it "should function if the :ignore value provided is nil" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = nil lambda { @fileset.files }.should_not raise_error end it "should ignore files that match a single pattern in the ignore list" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = ".svn" @fileset.files.find { |file| file.include?(".svn") }.should be_nil end it "should ignore files that match any of multiple patterns in the ignore list" do mock_dir_structure(@path) @fileset.recurse = true @fileset.ignore = %w{.svn CVS} @fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil end it "should use File.stat if :links is set to :follow" do mock_dir_structure(@path, :stat) @fileset.recurse = true @fileset.links = :follow @fileset.files.sort.should == @files.sort end it "should use File.lstat if :links is set to :manage" do mock_dir_structure(@path, :lstat) @fileset.recurse = true @fileset.links = :manage @fileset.files.sort.should == @files.sort end it "should succeed when paths have regexp significant characters" do - @path = "/my/path/rV1x2DafFr0R6tGG+1bbk++++TM" + @path = make_absolute("/my/path/rV1x2DafFr0R6tGG+1bbk++++TM") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) mock_dir_structure(@path) @fileset.recurse = true @fileset.files.sort.should == @files.sort end end describe Puppet::FileServing::Fileset, " when following links that point to missing files" do + include PuppetSpec::Files + before do - @path = "/my/path" + @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) @fileset.links = :follow @fileset.recurse = true @stat = stub 'stat', :directory? => true File.expects(:stat).with(@path).returns(@stat) File.expects(:stat).with(File.join(@path, "mylink")).raises(Errno::ENOENT) Dir.stubs(:entries).with(@path).returns(["mylink"]) end it "should not fail" do proc { @fileset.files }.should_not raise_error end it "should still manage the link" do @fileset.files.sort.should == %w{. mylink}.sort end end describe Puppet::FileServing::Fileset, " when ignoring" do + include PuppetSpec::Files + before do - @path = "/my/path" + @path = make_absolute("/my/path") File.expects(:lstat).with(@path).returns stub("stat", :directory? => true) @fileset = Puppet::FileServing::Fileset.new(@path) end it "should use ruby's globbing to determine what files should be ignored" do @fileset.ignore = ".svn" File.expects(:fnmatch?).with(".svn", "my_file") @fileset.ignore?("my_file") end it "should ignore files whose paths match a single provided ignore value" do @fileset.ignore = ".svn" File.stubs(:fnmatch?).with(".svn", "my_file").returns true @fileset.ignore?("my_file").should be_true end it "should ignore files whose paths match any of multiple provided ignore values" do @fileset.ignore = [".svn", "CVS"] File.stubs(:fnmatch?).with(".svn", "my_file").returns false File.stubs(:fnmatch?).with("CVS", "my_file").returns true @fileset.ignore?("my_file").should be_true end end describe Puppet::FileServing::Fileset, "when merging other filesets" do + include PuppetSpec::Files + before do - @paths = %w{/first/path /second/path /third/path} + @paths = [make_absolute("/first/path"), make_absolute("/second/path"), make_absolute("/third/path")] File.stubs(:lstat).returns stub("stat", :directory? => false) @filesets = @paths.collect do |path| File.stubs(:lstat).with(path).returns stub("stat", :directory? => true) Puppet::FileServing::Fileset.new(path, :recurse => true) end Dir.stubs(:entries).returns [] end it "should return a hash of all files in each fileset with the value being the base path" do - Dir.expects(:entries).with("/first/path").returns(%w{one uno}) - Dir.expects(:entries).with("/second/path").returns(%w{two dos}) - Dir.expects(:entries).with("/third/path").returns(%w{three tres}) + Dir.expects(:entries).with(make_absolute("/first/path")).returns(%w{one uno}) + Dir.expects(:entries).with(make_absolute("/second/path")).returns(%w{two dos}) + Dir.expects(:entries).with(make_absolute("/third/path")).returns(%w{three tres}) Puppet::FileServing::Fileset.merge(*@filesets).should == { - "." => "/first/path", - "one" => "/first/path", - "uno" => "/first/path", - "two" => "/second/path", - "dos" => "/second/path", - "three" => "/third/path", - "tres" => "/third/path", + "." => make_absolute("/first/path"), + "one" => make_absolute("/first/path"), + "uno" => make_absolute("/first/path"), + "two" => make_absolute("/second/path"), + "dos" => make_absolute("/second/path"), + "three" => make_absolute("/third/path"), + "tres" => make_absolute("/third/path"), } end it "should include the base directory from the first fileset" do - Dir.expects(:entries).with("/first/path").returns(%w{one}) - Dir.expects(:entries).with("/second/path").returns(%w{two}) + Dir.expects(:entries).with(make_absolute("/first/path")).returns(%w{one}) + Dir.expects(:entries).with(make_absolute("/second/path")).returns(%w{two}) - Puppet::FileServing::Fileset.merge(*@filesets)["."].should == "/first/path" + Puppet::FileServing::Fileset.merge(*@filesets)["."].should == make_absolute("/first/path") end it "should use the base path of the first found file when relative file paths conflict" do - Dir.expects(:entries).with("/first/path").returns(%w{one}) - Dir.expects(:entries).with("/second/path").returns(%w{one}) + Dir.expects(:entries).with(make_absolute("/first/path")).returns(%w{one}) + Dir.expects(:entries).with(make_absolute("/second/path")).returns(%w{one}) - Puppet::FileServing::Fileset.merge(*@filesets)["one"].should == "/first/path" + Puppet::FileServing::Fileset.merge(*@filesets)["one"].should == make_absolute("/first/path") end end diff --git a/spec/unit/indirector/certificate_request/ca_spec.rb b/spec/unit/indirector/certificate_request/ca_spec.rb index ebd64a2fb..fb758b59e 100755 --- a/spec/unit/indirector/certificate_request/ca_spec.rb +++ b/spec/unit/indirector/certificate_request/ca_spec.rb @@ -1,64 +1,64 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-7. # Copyright (c) 2007. All rights reserved. require 'spec_helper' require 'puppet/ssl/host' require 'puppet/sslcertificates' require 'puppet/sslcertificates/ca' require 'puppet/indirector/certificate_request/ca' -describe Puppet::SSL::CertificateRequest::Ca do +describe Puppet::SSL::CertificateRequest::Ca, :fails_on_windows => true do include PuppetSpec::Files before :each do Puppet[:ssldir] = tmpdir('ssl') Puppet::SSL::Host.ca_location = :local Puppet[:localcacert] = Puppet[:cacert] Puppet::SSLCertificates::CA.new.mkrootcert @ca = Puppet::SSL::CertificateAuthority.new end after :all do Puppet::SSL::Host.ca_location = :none end it "should have documentation" do Puppet::SSL::CertificateRequest::Ca.doc.should be_instance_of(String) end it "should use the :csrdir as the collection directory" do Puppet.settings.expects(:value).with(:csrdir).returns "/request/dir" Puppet::SSL::CertificateRequest::Ca.collection_directory.should == "/request/dir" end it "should overwrite the previous certificate request if allow_duplicate_certs is true" do Puppet[:allow_duplicate_certs] = true host = Puppet::SSL::Host.new("foo") host.generate_certificate_request @ca.sign(host.name) Puppet::SSL::Host.indirection.find("foo").generate_certificate_request Puppet::SSL::Certificate.indirection.find("foo").name.should == "foo" Puppet::SSL::CertificateRequest.indirection.find("foo").name.should == "foo" Puppet::SSL::Host.indirection.find("foo").state.should == "requested" end it "should reject a new certificate request if allow_duplicate_certs is false" do Puppet[:allow_duplicate_certs] = false host = Puppet::SSL::Host.new("bar") host.generate_certificate_request @ca.sign(host.name) expect { Puppet::SSL::Host.indirection.find("bar").generate_certificate_request }.should raise_error(/ignoring certificate request/) Puppet::SSL::Certificate.indirection.find("bar").name.should == "bar" Puppet::SSL::CertificateRequest.indirection.find("bar").should be_nil Puppet::SSL::Host.indirection.find("bar").state.should == "signed" end end diff --git a/spec/unit/indirector/certificate_status/file_spec.rb b/spec/unit/indirector/certificate_status/file_spec.rb index ae03aa9cb..897fe0716 100755 --- a/spec/unit/indirector/certificate_status/file_spec.rb +++ b/spec/unit/indirector/certificate_status/file_spec.rb @@ -1,187 +1,187 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/ssl/host' require 'puppet/indirector/certificate_status' require 'tempfile' -describe "Puppet::Indirector::CertificateStatus::File" do +describe "Puppet::Indirector::CertificateStatus::File", :fails_on_windows => true do include PuppetSpec::Files before do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns true @terminus = Puppet::SSL::Host.indirection.terminus(:file) @tmpdir = tmpdir("certificate_status_ca_testing") Puppet[:confdir] = @tmpdir Puppet[:vardir] = @tmpdir # localcacert is where each client stores the CA certificate # cacert is where the master stores the CA certificate # Since we need to play the role of both for testing we need them to be the same and exist Puppet[:cacert] = Puppet[:localcacert] end def generate_csr(host) host.generate_key csr = Puppet::SSL::CertificateRequest.new(host.name) csr.generate(host.key.content) Puppet::SSL::CertificateRequest.indirection.save(csr) end def sign_csr(host) host.desired_state = "signed" @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host)) end def generate_signed_cert(host) generate_csr(host) sign_csr(host) @terminus.find(Puppet::Indirector::Request.new(:certificate_status, :find, host.name, host)) end def generate_revoked_cert(host) generate_signed_cert(host) host.desired_state = "revoked" @terminus.save(Puppet::Indirector::Request.new(:certificate_status, :save, host.name, host)) end it "should be a terminus on SSL::Host" do @terminus.should be_instance_of(Puppet::Indirector::CertificateStatus::File) end it "should create a CA instance if none is present" do @terminus.ca.should be_instance_of(Puppet::SSL::CertificateAuthority) end describe "when creating the CA" do it "should fail if it is not a valid CA" do Puppet::SSL::CertificateAuthority.expects(:ca?).returns false lambda { @terminus.ca }.should raise_error(ArgumentError, "This process is not configured as a certificate authority") end end it "should be indirected with the name 'certificate_status'" do Puppet::SSL::Host.indirection.name.should == :certificate_status end describe "when finding" do before do @host = Puppet::SSL::Host.new("foo") Puppet.settings.use(:main) end it "should return the Puppet::SSL::Host when a CSR exists for the host" do generate_csr(@host) request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) retrieved_host = @terminus.find(request) retrieved_host.name.should == @host.name retrieved_host.certificate_request.content.to_s.chomp.should == @host.certificate_request.content.to_s.chomp end it "should return the Puppet::SSL::Host when a public key exist for the host" do generate_signed_cert(@host) request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) retrieved_host = @terminus.find(request) retrieved_host.name.should == @host.name retrieved_host.certificate.content.to_s.chomp.should == @host.certificate.content.to_s.chomp end it "should return nil when neither a CSR nor public key exist for the host" do request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host) @terminus.find(request).should == nil end end describe "when saving" do before do @host = Puppet::SSL::Host.new("foobar") Puppet.settings.use(:main) end describe "when signing a cert" do before do @host.desired_state = "signed" @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host) end it "should fail if no CSR is on disk" do lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /certificate request/) end it "should sign the on-disk CSR when it is present" do signed_host = generate_signed_cert(@host) signed_host.state.should == "signed" Puppet::SSL::Certificate.indirection.find("foobar").should be_instance_of(Puppet::SSL::Certificate) end end describe "when revoking a cert" do before do @request = Puppet::Indirector::Request.new(:certificate_status, :save, "foobar", @host) end it "should fail if no certificate is on disk" do @host.desired_state = "revoked" lambda { @terminus.save(@request) }.should raise_error(Puppet::Error, /Cannot revoke/) end it "should revoke the certificate when it is present" do generate_revoked_cert(@host) @host.state.should == 'revoked' end end end describe "when deleting" do before do Puppet.settings.use(:main) end it "should not delete anything if no certificate, request, or key is on disk" do host = Puppet::SSL::Host.new("clean_me") request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_me", host) @terminus.destroy(request).should == "Nothing was deleted" end it "should clean certs, cert requests, keys" do signed_host = Puppet::SSL::Host.new("clean_signed_cert") generate_signed_cert(signed_host) signed_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_signed_cert", signed_host) @terminus.destroy(signed_request).should == "Deleted for clean_signed_cert: Puppet::SSL::Certificate, Puppet::SSL::Key" requested_host = Puppet::SSL::Host.new("clean_csr") generate_csr(requested_host) csr_request = Puppet::Indirector::Request.new(:certificate_status, :delete, "clean_csr", requested_host) @terminus.destroy(csr_request).should == "Deleted for clean_csr: Puppet::SSL::CertificateRequest, Puppet::SSL::Key" end end describe "when searching" do it "should return a list of all hosts with certificate requests, signed certs, or revoked certs" do Puppet.settings.use(:main) signed_host = Puppet::SSL::Host.new("signed_host") generate_signed_cert(signed_host) requested_host = Puppet::SSL::Host.new("requested_host") generate_csr(requested_host) revoked_host = Puppet::SSL::Host.new("revoked_host") generate_revoked_cert(revoked_host) retrieved_hosts = @terminus.search(Puppet::Indirector::Request.new(:certificate_status, :search, "all", signed_host)) results = retrieved_hosts.map {|h| [h.name, h.state]}.sort{ |h,i| h[0] <=> i[0] } results.should == [["ca","signed"],["requested_host","requested"],["revoked_host","revoked"],["signed_host","signed"]] end end end diff --git a/spec/unit/indirector/file_bucket_file/file_spec.rb b/spec/unit/indirector/file_bucket_file/file_spec.rb index e0612cb21..ee0b61af1 100755 --- a/spec/unit/indirector/file_bucket_file/file_spec.rb +++ b/spec/unit/indirector/file_bucket_file/file_spec.rb @@ -1,273 +1,273 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/indirector/file_bucket_file/file' describe Puppet::FileBucketFile::File do include PuppetSpec::Files it "should be a subclass of the Code terminus class" do Puppet::FileBucketFile::File.superclass.should equal(Puppet::Indirector::Code) end it "should have documentation" do Puppet::FileBucketFile::File.doc.should be_instance_of(String) end - describe "non-stubbing tests" do + describe "non-stubbing tests", :fails_on_windows => true do include PuppetSpec::Files before do Puppet[:bucketdir] = tmpdir('bucketdir') end def save_bucket_file(contents, path = "/who_cares") bucket_file = Puppet::FileBucket::File.new(contents) Puppet::FileBucket::File.indirection.save(bucket_file, "md5/#{Digest::MD5.hexdigest(contents)}#{path}") bucket_file.checksum_data end describe "when servicing a save request" do describe "when supplying a path" do it "should store the path if not already stored" do checksum = save_bucket_file("stuff", "/foo/bar") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\n" end it "should leave the paths file alone if the path is already stored" do checksum = save_bucket_file("stuff", "/foo/bar") checksum = save_bucket_file("stuff", "/foo/bar") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\n" end it "should store an additional path if the new path differs from those already stored" do checksum = save_bucket_file("stuff", "/foo/bar") checksum = save_bucket_file("stuff", "/foo/baz") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "foo/bar\nfoo/baz\n" end end describe "when not supplying a path" do it "should save the file and create an empty paths file" do checksum = save_bucket_file("stuff", "") dir_path = "#{Puppet[:bucketdir]}/c/1/3/d/8/8/c/b/c13d88cb4cb02003daedb8a84e5d272a" File.read("#{dir_path}/contents").should == "stuff" File.read("#{dir_path}/paths").should == "" end end end describe "when servicing a head/find request" do describe "when supplying a path" do it "should return false/nil if the file isn't bucketed" do Puppet::FileBucket::File.indirection.head("md5/0ae2ec1980410229885fe72f7b44fe55/foo/bar").should == false Puppet::FileBucket::File.indirection.find("md5/0ae2ec1980410229885fe72f7b44fe55/foo/bar").should == nil end it "should return false/nil if the file is bucketed but with a different path" do checksum = save_bucket_file("I'm the contents of a file", '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}/foo/baz").should == false Puppet::FileBucket::File.indirection.find("md5/#{checksum}/foo/baz").should == nil end it "should return true/file if the file is already bucketed with the given path" do contents = "I'm the contents of a file" checksum = save_bucket_file(contents, '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}/foo/bar").should == true find_result = Puppet::FileBucket::File.indirection.find("md5/#{checksum}/foo/bar") find_result.should be_a(Puppet::FileBucket::File) find_result.checksum.should == "{md5}#{checksum}" find_result.to_s.should == contents end end describe "when not supplying a path" do [false, true].each do |trailing_slash| describe "#{trailing_slash ? 'with' : 'without'} a trailing slash" do trailing_string = trailing_slash ? '/' : '' it "should return false/nil if the file isn't bucketed" do Puppet::FileBucket::File.indirection.head("md5/0ae2ec1980410229885fe72f7b44fe55#{trailing_string}").should == false Puppet::FileBucket::File.indirection.find("md5/0ae2ec1980410229885fe72f7b44fe55#{trailing_string}").should == nil end it "should return true/file if the file is already bucketed" do contents = "I'm the contents of a file" checksum = save_bucket_file(contents, '/foo/bar') Puppet::FileBucket::File.indirection.head("md5/#{checksum}#{trailing_string}").should == true find_result = Puppet::FileBucket::File.indirection.find("md5/#{checksum}#{trailing_string}") find_result.should be_a(Puppet::FileBucket::File) find_result.checksum.should == "{md5}#{checksum}" find_result.to_s.should == contents end end end end end describe "when diffing files" do it "should generate an empty string if there is no diff" do checksum = save_bucket_file("I'm the contents of a file") Puppet::FileBucket::File.indirection.find("md5/#{checksum}", :diff_with => checksum).should == '' end it "should generate a proper diff if there is a diff" do checksum1 = save_bucket_file("foo\nbar\nbaz") checksum2 = save_bucket_file("foo\nbiz\nbaz") diff = Puppet::FileBucket::File.indirection.find("md5/#{checksum1}", :diff_with => checksum2) diff.should == < biz HERE end it "should raise an exception if the hash to diff against isn't found" do checksum = save_bucket_file("whatever") bogus_checksum = "d1bf072d0e2c6e20e3fbd23f022089a1" lambda { Puppet::FileBucket::File.indirection.find("md5/#{checksum}", :diff_with => bogus_checksum) }.should raise_error "could not find diff_with #{bogus_checksum}" end it "should return nil if the hash to diff from isn't found" do checksum = save_bucket_file("whatever") bogus_checksum = "d1bf072d0e2c6e20e3fbd23f022089a1" Puppet::FileBucket::File.indirection.find("md5/#{bogus_checksum}", :diff_with => checksum).should == nil end end end describe "when initializing" do it "should use the filebucket settings section" do Puppet.settings.expects(:use).with(:filebucket) Puppet::FileBucketFile::File.new end end [true, false].each do |override_bucket_path| describe "when bucket path #{if override_bucket_path then 'is' else 'is not' end} overridden" do [true, false].each do |supply_path| describe "when #{supply_path ? 'supplying' : 'not supplying'} a path" do before :each do Puppet.settings.stubs(:use) @store = Puppet::FileBucketFile::File.new @contents = "my content" @digest = "f2bfa7fc155c4f42cb91404198dda01f" @digest.should == Digest::MD5.hexdigest(@contents) @bucket_dir = tmpdir("bucket") if override_bucket_path Puppet[:bucketdir] = "/bogus/path" # should not be used else Puppet[:bucketdir] = @bucket_dir end @dir = "#{@bucket_dir}/f/2/b/f/a/7/f/c/f2bfa7fc155c4f42cb91404198dda01f" @contents_path = "#{@dir}/contents" end describe "when retrieving files" do before :each do request_options = {} if override_bucket_path request_options[:bucket_path] = @bucket_dir end key = "md5/#{@digest}" if supply_path key += "/path/to/file" end @request = Puppet::Indirector::Request.new(:indirection_name, :find, key, request_options) end def make_bucketed_file FileUtils.mkdir_p(@dir) File.open(@contents_path, 'w') { |f| f.write @contents } end it "should return an instance of Puppet::FileBucket::File created with the content if the file exists" do make_bucketed_file if supply_path @store.find(@request).should == nil @store.head(@request).should == false # because path didn't match else bucketfile = @store.find(@request) bucketfile.should be_a(Puppet::FileBucket::File) bucketfile.contents.should == @contents @store.head(@request).should == true end end it "should return nil if no file is found" do @store.find(@request).should be_nil @store.head(@request).should == false end end describe "when saving files" do it "should save the contents to the calculated path" do options = {} if override_bucket_path options[:bucket_path] = @bucket_dir end key = "md5/#{@digest}" if supply_path key += "//path/to/file" end file_instance = Puppet::FileBucket::File.new(@contents, options) request = Puppet::Indirector::Request.new(:indirection_name, :save, key, file_instance) @store.save(request) File.read("#{@dir}/contents").should == @contents end end end end end end describe "when verifying identical files" do before do # this is the default from spec_helper, but it keeps getting reset at odd times - Puppet[:bucketdir] = "/dev/null/bucket" + Puppet[:bucketdir] = make_absolute("/dev/null/bucket") @digest = "4a8ec4fa5f01b4ab1a0ab8cbccb709f0" @checksum = "{md5}4a8ec4fa5f01b4ab1a0ab8cbccb709f0" - @dir = '/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0' + @dir = make_absolute('/dev/null/bucket/4/a/8/e/c/4/f/a/4a8ec4fa5f01b4ab1a0ab8cbccb709f0') @contents = "file contents" @bucket = stub "bucket file" @bucket.stubs(:bucket_path) @bucket.stubs(:checksum).returns(@checksum) @bucket.stubs(:checksum_data).returns(@digest) @bucket.stubs(:path).returns(nil) @bucket.stubs(:contents).returns("file contents") end it "should raise an error if the files don't match" do File.expects(:read).with("#{@dir}/contents").returns("corrupt contents") lambda{ Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) }.should raise_error(Puppet::FileBucket::BucketError) end it "should do nothing if the files match" do File.expects(:read).with("#{@dir}/contents").returns("file contents") Puppet::FileBucketFile::File.new.send(:verify_identical_file!, @bucket) end end end diff --git a/spec/unit/indirector/resource/ral_spec.rb b/spec/unit/indirector/resource/ral_spec.rb index cf746cb0c..e38745f05 100755 --- a/spec/unit/indirector/resource/ral_spec.rb +++ b/spec/unit/indirector/resource/ral_spec.rb @@ -1,128 +1,128 @@ #!/usr/bin/env rspec require 'spec_helper' describe "Puppet::Resource::Ral" do - describe "find" do + describe "find", :fails_on_windows => true do before do @request = stub 'request', :key => "user/root" end it "should find an existing instance" do my_resource = stub "my user resource" wrong_instance = stub "wrong user", :name => "bob" my_instance = stub "my user", :name => "root", :to_resource => my_resource require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ wrong_instance, my_instance, wrong_instance ]) Puppet::Resource::Ral.new.find(@request).should == my_resource end it "if there is no instance, it should create one", :'fails_on_ruby_1.9.2' => true do wrong_instance = stub "wrong user", :name => "bob" require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ wrong_instance, wrong_instance ]) result = Puppet::Resource::Ral.new.find(@request) result.should be_is_a(Puppet::Resource) result.title.should == "root" end end describe "search" do before do @request = stub 'request', :key => "user/", :options => {} end it "should convert ral resources into regular resources" do my_resource = stub "my user resource" my_instance = stub "my user", :name => "root", :to_resource => my_resource require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should filter results by name if there's a name in the key" do my_resource = stub "my user resource" my_resource.stubs(:to_resource).returns(my_resource) my_resource.stubs(:[]).with(:name).returns("root") wrong_resource = stub "wrong resource" wrong_resource.stubs(:to_resource).returns(wrong_resource) wrong_resource.stubs(:[]).with(:name).returns("bad") my_instance = stub "my user", :to_resource => my_resource wrong_instance = stub "wrong user", :to_resource => wrong_resource @request = stub 'request', :key => "user/root", :options => {} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should filter results by query parameters" do wrong_resource = stub "my user resource" wrong_resource.stubs(:to_resource).returns(wrong_resource) wrong_resource.stubs(:[]).with(:name).returns("root") my_resource = stub "wrong resource" my_resource.stubs(:to_resource).returns(my_resource) my_resource.stubs(:[]).with(:name).returns("bob") my_instance = stub "my user", :to_resource => my_resource wrong_instance = stub "wrong user", :to_resource => wrong_resource @request = stub 'request', :key => "user/", :options => {:name => "bob"} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ my_instance, wrong_instance ]) Puppet::Resource::Ral.new.search(@request).should == [my_resource] end it "should return sorted results" do a_resource = stub "alice resource" a_resource.stubs(:to_resource).returns(a_resource) a_resource.stubs(:title).returns("alice") b_resource = stub "bob resource" b_resource.stubs(:to_resource).returns(b_resource) b_resource.stubs(:title).returns("bob") a_instance = stub "alice user", :to_resource => a_resource b_instance = stub "bob user", :to_resource => b_resource @request = stub 'request', :key => "user/", :options => {} require 'puppet/type/user' Puppet::Type::User.expects(:instances).returns([ b_instance, a_instance ]) Puppet::Resource::Ral.new.search(@request).should == [a_resource, b_resource] end end describe "save" do before do @rebuilt_res = stub 'rebuilt instance' @ral_res = stub 'ral resource', :to_resource => @rebuilt_res @instance = stub 'instance', :to_ral => @ral_res @request = stub 'request', :key => "user/", :instance => @instance @catalog = stub 'catalog' Puppet::Resource::Catalog.stubs(:new).returns(@catalog) @catalog.stubs(:apply) @catalog.stubs(:add_resource) end it "should apply a new catalog with a ral object in it" do Puppet::Resource::Catalog.expects(:new).returns(@catalog) @catalog.expects(:add_resource).with(@ral_res) @catalog.expects(:apply) Puppet::Resource::Ral.new.save(@request) end it "should return a regular resource that used to be the ral resource" do Puppet::Resource::Ral.new.save(@request).should == @rebuilt_res end end end diff --git a/spec/unit/indirector/resource_type/parser_spec.rb b/spec/unit/indirector/resource_type/parser_spec.rb index c4fc455a0..67ea73fd6 100755 --- a/spec/unit/indirector/resource_type/parser_spec.rb +++ b/spec/unit/indirector/resource_type/parser_spec.rb @@ -1,149 +1,149 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/indirector/resource_type/parser' require 'puppet_spec/files' describe Puppet::Indirector::ResourceType::Parser do include PuppetSpec::Files before do @terminus = Puppet::Indirector::ResourceType::Parser.new @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo") @krt = @request.environment.known_resource_types end it "should be registered with the resource_type indirection" do Puppet::Indirector::Terminus.terminus_class(:resource_type, :parser).should equal(Puppet::Indirector::ResourceType::Parser) end describe "when finding" do it "should return any found type from the request's environment" do type = Puppet::Resource::Type.new(:hostclass, "foo") @request.environment.known_resource_types.add(type) @terminus.find(@request).should == type end - it "should attempt to load the type if none is found in memory" do + it "should attempt to load the type if none is found in memory", :fails_on_windows => true do dir = tmpdir("find_a_type") FileUtils.mkdir_p(dir) Puppet[:modulepath] = dir # Make a new request, since we've reset the env @request = Puppet::Indirector::Request.new(:resource_type, :find, "foo::bar") manifest_path = File.join(dir, "foo", "manifests") FileUtils.mkdir_p(manifest_path) File.open(File.join(manifest_path, "bar.pp"), "w") { |f| f.puts "class foo::bar {}" } result = @terminus.find(@request) result.should be_instance_of(Puppet::Resource::Type) result.name.should == "foo::bar" end it "should return nil if no type can be found" do @terminus.find(@request).should be_nil end it "should prefer definitions to nodes" do type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "foo")) @terminus.find(@request).should == type end end describe "when searching" do before do @request.key = "*" end it "should use the request's environment's list of known resource types" do @request.environment.known_resource_types.expects(:hostclasses).returns({}) @terminus.search(@request) end it "should return all results if '*' is provided as the search string" do @request.key = "*" type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) result = @terminus.search(@request) result.should be_include(type) result.should be_include(node) result.should be_include(define) end it "should treat any search string not '*' as a regex" do @request.key = "a" foo = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) bar = @krt.add(Puppet::Resource::Type.new(:hostclass, "bar")) baz = @krt.add(Puppet::Resource::Type.new(:hostclass, "baz")) result = @terminus.search(@request) result.should be_include(bar) result.should be_include(baz) result.should_not be_include(foo) end it "should fail if a provided search string is not '*' and is not a valid regex" do @request.key = "*foo*" # Add one instance so we don't just get an empty array" @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) lambda { @terminus.search(@request) }.should raise_error(ArgumentError) end it "should return all known types" do type = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) node = @krt.add(Puppet::Resource::Type.new(:node, "bar")) define = @krt.add(Puppet::Resource::Type.new(:definition, "baz")) result = @terminus.search(@request) result.should be_include(type) result.should be_include(node) result.should be_include(define) end it "should not return the 'main' class" do main = @krt.add(Puppet::Resource::Type.new(:hostclass, "")) # So there is a return value foo = @krt.add(Puppet::Resource::Type.new(:hostclass, "foo")) @terminus.search(@request).should_not be_include(main) end it "should return nil if no types can be found" do @terminus.search(@request).should be_nil end - it "should load all resource types from all search paths" do + it "should load all resource types from all search paths", :fails_on_windows => true do dir = tmpdir("searching_in_all") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) - Puppet[:modulepath] = "#{first}:#{second}" + Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}" # Make a new request, since we've reset the env @request = Puppet::Indirector::Request.new(:resource_type, :search, "*") onepath = File.join(first, "one", "manifests") FileUtils.mkdir_p(onepath) twopath = File.join(first, "two", "manifests") FileUtils.mkdir_p(twopath) File.open(File.join(onepath, "oneklass.pp"), "w") { |f| f.puts "class one::oneklass {}" } File.open(File.join(twopath, "twoklass.pp"), "w") { |f| f.puts "class two::twoklass {}" } result = @terminus.search(@request) result.find { |t| t.name == "one::oneklass" }.should be_instance_of(Puppet::Resource::Type) result.find { |t| t.name == "two::twoklass" }.should be_instance_of(Puppet::Resource::Type) end end end diff --git a/spec/unit/indirector/ssl_file_spec.rb b/spec/unit/indirector/ssl_file_spec.rb index 5d0859598..1a837f646 100755 --- a/spec/unit/indirector/ssl_file_spec.rb +++ b/spec/unit/indirector/ssl_file_spec.rb @@ -1,282 +1,284 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2008-3-10. # Copyright (c) 2007. All rights reserved. require 'spec_helper' require 'puppet/indirector/ssl_file' describe Puppet::Indirector::SslFile do + include PuppetSpec::Files + before :all do @indirection = stub 'indirection', :name => :testing, :model => @model Puppet::Indirector::Indirection.expects(:instance).with(:testing).returns(@indirection) module Testing; end @file_class = class Testing::MyType < Puppet::Indirector::SslFile self end end before :each do @model = mock 'model' @setting = :certdir @file_class.store_in @setting - @path = "/tmp/my_directory" + @path = make_absolute("/tmp/my_directory") Puppet[:noop] = false Puppet[@setting] = @path Puppet[:trace] = false end it "should use :main and :ssl upon initialization" do Puppet.settings.expects(:use).with(:main, :ssl) @file_class.new end it "should return a nil collection directory if no directory setting has been provided" do @file_class.store_in nil @file_class.collection_directory.should be_nil end it "should return a nil file location if no location has been provided" do @file_class.store_at nil @file_class.file_location.should be_nil end it "should fail if no store directory or file location has been set" do @file_class.store_in nil @file_class.store_at nil lambda { @file_class.new }.should raise_error(Puppet::DevError) end describe "when managing ssl files" do before do Puppet.settings.stubs(:use) @searcher = @file_class.new @cert = stub 'certificate', :name => "myname" @certpath = File.join(@path, "myname.pem") @request = stub 'request', :key => @cert.name, :instance => @cert end it "should consider the file a ca file if the name is equal to what the SSL::Host class says is the CA name" do Puppet::SSL::Host.expects(:ca_name).returns "amaca" @searcher.should be_ca("amaca") end describe "when choosing the location for certificates" do it "should set them at the ca setting's path if a ca setting is available and the name resolves to the CA name" do @file_class.store_in nil @file_class.store_at :mysetting @file_class.store_ca_at :casetting Puppet.settings.stubs(:value).with(:casetting).returns "/ca/file" @searcher.expects(:ca?).with(@cert.name).returns true @searcher.path(@cert.name).should == "/ca/file" end it "should set them at the file location if a file setting is available" do @file_class.store_in nil @file_class.store_at :mysetting Puppet.settings.stubs(:value).with(:mysetting).returns "/some/file" @searcher.path(@cert.name).should == "/some/file" end it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" do @searcher.path(@cert.name).should == @certpath end end describe "when finding certificates on disk" do describe "and no certificate is present" do before do # Stub things so the case management bits work. FileTest.stubs(:exist?).with(File.dirname(@certpath)).returns false FileTest.expects(:exist?).with(@certpath).returns false end it "should return nil" do @searcher.find(@request).should be_nil end end describe "and a certificate is present" do before do FileTest.expects(:exist?).with(@certpath).returns true end it "should return an instance of the model, which it should use to read the certificate" do cert = mock 'cert' model = mock 'model' @file_class.stubs(:model).returns model model.expects(:new).with("myname").returns cert cert.expects(:read).with(@certpath) @searcher.find(@request).should equal(cert) end end describe "and a certificate is present but has uppercase letters" do before do @request = stub 'request', :key => "myhost" end # This is kind of more an integration test; it's for #1382, until # the support for upper-case certs can be removed around mid-2009. it "should rename the existing file to the lower-case path" do @path = @searcher.path("myhost") FileTest.expects(:exist?).with(@path).returns(false) dir, file = File.split(@path) FileTest.expects(:exist?).with(dir).returns true Dir.expects(:entries).with(dir).returns [".", "..", "something.pem", file.upcase] File.expects(:rename).with(File.join(dir, file.upcase), @path) cert = mock 'cert' model = mock 'model' @searcher.stubs(:model).returns model @searcher.model.expects(:new).with("myhost").returns cert cert.expects(:read).with(@path) @searcher.find(@request) end end end describe "when saving certificates to disk" do before do FileTest.stubs(:directory?).returns true FileTest.stubs(:writable?).returns true end it "should fail if the directory is absent" do FileTest.expects(:directory?).with(File.dirname(@certpath)).returns false lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should fail if the directory is not writeable" do FileTest.stubs(:directory?).returns true FileTest.expects(:writable?).with(File.dirname(@certpath)).returns false lambda { @searcher.save(@request) }.should raise_error(Puppet::Error) end it "should save to the path the output of converting the certificate to a string" do fh = mock 'filehandle' fh.expects(:print).with("mycert") @searcher.stubs(:write).yields fh @cert.expects(:to_s).returns "mycert" @searcher.save(@request) end describe "and a directory setting is set" do it "should use the Settings class to write the file" do @searcher.class.store_in @setting fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:writesub).with(@setting, @certpath).yields fh @searcher.save(@request) end end describe "and a file location is set" do it "should use the filehandle provided by the Settings" do @searcher.class.store_at @setting fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:write).with(@setting).yields fh @searcher.save(@request) end end describe "and the name is the CA name and a ca setting is set" do it "should use the filehandle provided by the Settings" do @searcher.class.store_at @setting @searcher.class.store_ca_at :castuff Puppet.settings.stubs(:value).with(:castuff).returns "castuff stub" fh = mock 'filehandle' fh.stubs :print Puppet.settings.expects(:write).with(:castuff).yields fh @searcher.stubs(:ca?).returns true @searcher.save(@request) end end end describe "when destroying certificates" do describe "that do not exist" do before do FileTest.expects(:exist?).with(@certpath).returns false end it "should return false" do @searcher.destroy(@request).should be_false end end describe "that exist" do before do FileTest.expects(:exist?).with(@certpath).returns true end it "should unlink the certificate file" do File.expects(:unlink).with(@certpath) @searcher.destroy(@request) end it "should log that is removing the file" do File.stubs(:exist?).returns true File.stubs(:unlink) Puppet.expects(:notice) @searcher.destroy(@request) end end end describe "when searching for certificates" do before do @model = mock 'model' @file_class.stubs(:model).returns @model end it "should return a certificate instance for all files that exist" do Dir.expects(:entries).with(@path).returns %w{one.pem two.pem} one = stub 'one', :read => nil two = stub 'two', :read => nil @model.expects(:new).with("one").returns one @model.expects(:new).with("two").returns two @searcher.search(@request).should == [one, two] end it "should read each certificate in using the model's :read method" do Dir.expects(:entries).with(@path).returns %w{one.pem} one = stub 'one' one.expects(:read).with(File.join(@path, "one.pem")) @model.expects(:new).with("one").returns one @searcher.search(@request) end it "should skip any files that do not match /\.pem$/" do Dir.expects(:entries).with(@path).returns %w{. .. one.pem} one = stub 'one', :read => nil @model.expects(:new).with("one").returns one @searcher.search(@request) end end end end diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb index 8d38657f9..d1d01a1aa 100755 --- a/spec/unit/module_spec.rb +++ b/spec/unit/module_spec.rb @@ -1,569 +1,569 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet_spec/files' describe Puppet::Module do include PuppetSpec::Files before do # This is necessary because of the extra checks we have for the deprecated # 'plugins' directory FileTest.stubs(:exist?).returns false end it "should have a class method that returns a named module from a given environment" do env = mock 'module' env.expects(:module).with("mymod").returns "yep" Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should == "yep" end it "should return nil if asked for a named module that doesn't exist" do env = mock 'module' env.expects(:module).with("mymod").returns nil Puppet::Node::Environment.expects(:new).with("myenv").returns env Puppet::Module.find("mymod", "myenv").should be_nil end it "should support a 'version' attribute" do mod = Puppet::Module.new("mymod") mod.version = 1.09 mod.version.should == 1.09 end it "should support a 'source' attribute" do mod = Puppet::Module.new("mymod") mod.source = "http://foo/bar" mod.source.should == "http://foo/bar" end it "should support a 'project_page' attribute" do mod = Puppet::Module.new("mymod") mod.project_page = "http://foo/bar" mod.project_page.should == "http://foo/bar" end it "should support an 'author' attribute" do mod = Puppet::Module.new("mymod") mod.author = "Luke Kanies " mod.author.should == "Luke Kanies " end it "should support a 'license' attribute" do mod = Puppet::Module.new("mymod") mod.license = "GPL2" mod.license.should == "GPL2" end it "should support a 'summary' attribute" do mod = Puppet::Module.new("mymod") mod.summary = "GPL2" mod.summary.should == "GPL2" end it "should support a 'description' attribute" do mod = Puppet::Module.new("mymod") mod.description = "GPL2" mod.description.should == "GPL2" end it "should support specifying a compatible puppet version" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" mod.puppetversion.should == "0.25" end it "should validate that the puppet version is compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.expects(:version).returns "0.25" mod.validate_puppet_version end it "should fail if the specified puppet version is not compatible" do mod = Puppet::Module.new("mymod") mod.puppetversion = "0.25" Puppet.stubs(:version).returns "0.24" lambda { mod.validate_puppet_version }.should raise_error(Puppet::Module::IncompatibleModule) end describe "when specifying required modules" do it "should support specifying a required module" do mod = Puppet::Module.new("mymod") mod.requires "foobar" end it "should support specifying multiple required modules" do mod = Puppet::Module.new("mymod") mod.requires "foobar" mod.requires "baz" end it "should support specifying a required module and version" do mod = Puppet::Module.new("mymod") mod.requires "foobar", 1.0 end it "should fail when required modules are missing" do mod = Puppet::Module.new("mymod") mod.requires "foobar" mod.environment.expects(:module).with("foobar").returns nil lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) end it "should fail when required modules are present but of the wrong version" do mod = Puppet::Module.new("mymod") mod.requires "foobar", 1.0 foobar = Puppet::Module.new("foobar") foobar.version = 2.0 mod.environment.expects(:module).with("foobar").returns foobar lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::IncompatibleModule) end it "should have valid dependencies when no dependencies have been specified" do mod = Puppet::Module.new("mymod") lambda { mod.validate_dependencies }.should_not raise_error end it "should fail when some dependencies are present but others aren't" do mod = Puppet::Module.new("mymod") mod.requires "foobar" mod.requires "baz" mod.environment.expects(:module).with("foobar").returns Puppet::Module.new("foobar") mod.environment.expects(:module).with("baz").returns nil lambda { mod.validate_dependencies }.should raise_error(Puppet::Module::MissingModule) end it "should have valid dependencies when all dependencies are met" do mod = Puppet::Module.new("mymod") mod.requires "foobar", 1.0 mod.requires "baz" foobar = Puppet::Module.new("foobar") foobar.version = 1.0 baz = Puppet::Module.new("baz") mod.environment.expects(:module).with("foobar").returns foobar mod.environment.expects(:module).with("baz").returns baz lambda { mod.validate_dependencies }.should_not raise_error end it "should validate its dependendencies on initialization" do Puppet::Module.any_instance.expects(:validate_dependencies) Puppet::Module.new("mymod") end end describe "when managing supported platforms" do it "should support specifying a supported platform" do mod = Puppet::Module.new("mymod") mod.supports "solaris" end it "should support specifying a supported platform and version" do mod = Puppet::Module.new("mymod") mod.supports "solaris", 1.0 end it "should fail when not running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" mod.supports "hpux" lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::UnsupportedPlatform) end it "should fail when supported platforms are present but of the wrong version" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when no supported platforms have been specified" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") lambda { mod.validate_supported_platform }.should_not raise_error end it "should be considered supported when running on a supported platform" do pending "Not sure how to send client platform to the module" mod = Puppet::Module.new("mymod") Facter.expects(:value).with("operatingsystem").returns "Solaris" Facter.expects(:value).with("operatingsystemrelease").returns 2.0 mod.supports "Solaris", 1.0 lambda { mod.validate_supported_platform }.should raise_error(Puppet::Module::IncompatiblePlatform) end it "should be considered supported when running on any of multiple supported platforms" do pending "Not sure how to send client platform to the module" end it "should validate its platform support on initialization" do pending "Not sure how to send client platform to the module" end end it "should return nil if asked for a module whose name is 'nil'" do Puppet::Module.find(nil, "myenv").should be_nil end it "should provide support for logging" do Puppet::Module.ancestors.should be_include(Puppet::Util::Logging) end it "should be able to be converted to a string" do Puppet::Module.new("foo").to_s.should == "Module foo" end it "should add the path to its string form if the module is found" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a" mod.to_s.should == "Module foo(/a)" end it "should fail if its name is not alphanumeric" do lambda { Puppet::Module.new(".something") }.should raise_error(Puppet::Module::InvalidName) end it "should require a name at initialization" do lambda { Puppet::Module.new }.should raise_error(ArgumentError) end it "should convert an environment name into an Environment instance" do Puppet::Module.new("foo", "prod").environment.should be_instance_of(Puppet::Node::Environment) end it "should accept an environment at initialization" do Puppet::Module.new("foo", :prod).environment.name.should == :prod end it "should use the default environment if none is provided" do env = Puppet::Node::Environment.new Puppet::Module.new("foo").environment.should equal(env) end it "should use any provided Environment instance" do env = Puppet::Node::Environment.new Puppet::Module.new("foo", env).environment.should equal(env) end it "should return the path to the first found instance in its environment's module paths as its path" do dir = tmpdir("deep_path") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) - Puppet[:modulepath] = "#{first}:#{second}" + Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}" modpath = File.join(first, "foo") FileUtils.mkdir_p(modpath) # Make a second one, which we shouldn't find FileUtils.mkdir_p(File.join(second, "foo")) mod = Puppet::Module.new("foo") mod.path.should == modpath end it "should be able to find itself in a directory other than the first directory in the module path" do dir = tmpdir("deep_path") first = File.join(dir, "first") second = File.join(dir, "second") FileUtils.mkdir_p(first) FileUtils.mkdir_p(second) - Puppet[:modulepath] = "#{first}:#{second}" + Puppet[:modulepath] = "#{first}#{File::PATH_SEPARATOR}#{second}" modpath = File.join(second, "foo") FileUtils.mkdir_p(modpath) mod = Puppet::Module.new("foo") mod.should be_exist mod.path.should == modpath end it "should be considered existent if it exists in at least one module path" do mod = Puppet::Module.new("foo") mod.expects(:path).returns "/a/foo" mod.should be_exist end it "should be considered nonexistent if it does not exist in any of the module paths" do mod = Puppet::Module.new("foo") mod.expects(:path).returns nil mod.should_not be_exist end [:plugins, :templates, :files, :manifests].each do |filetype| dirname = filetype == :plugins ? "lib" : filetype.to_s it "should be able to return individual #{filetype}" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == path end it "should consider #{filetype} to be present if their base directory exists" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns true mod.send(filetype.to_s + "?").should be_true end it "should consider #{filetype} to be absent if their base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s + "?").should be_false end it "should consider #{filetype} to be absent if the module base directory does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s + "?").should be_false end it "should return nil if asked to return individual #{filetype} that don't exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" path = File.join("/a/foo", dirname, "my/file") FileTest.expects(:exist?).with(path).returns false mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return nil when asked for individual #{filetype} if the module does not exist" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns nil mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should be_nil end it "should return the base directory if asked for a nil path" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" base = File.join("/a/foo", dirname) FileTest.expects(:exist?).with(base).returns true mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base end end %w{plugins files}.each do |filetype| short = filetype.sub(/s$/, '') dirname = filetype == "plugins" ? "lib" : filetype.to_s it "should be able to return the #{short} directory" do Puppet::Module.new("foo").should respond_to(short + "_directory") end it "should return the path to the #{short} directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.send(short + "_directory").should == "/a/foo/#{dirname}" end end it "should throw a warning if plugins are in a 'plugins' directory rather than a 'lib' directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" FileTest.expects(:exist?).with("/a/foo/plugins").returns true mod.plugin_directory.should == "/a/foo/plugins" @logs.first.message.should == "using the deprecated 'plugins' directory for ruby extensions; please move to 'lib'" @logs.first.level.should == :warning end it "should default to 'lib' for the plugins directory" do mod = Puppet::Module.new("foo") mod.stubs(:path).returns "/a/foo" mod.plugin_directory.should == "/a/foo/lib" end end describe Puppet::Module, " when building its search path" do it "should use the current environment's search path if no environment is specified" do env = mock 'env' env.expects(:modulepath).returns "eh" Puppet::Node::Environment.expects(:new).with(nil).returns env Puppet::Module.modulepath.should == "eh" end it "should use the specified environment's search path if an environment is specified" do env = mock 'env' env.expects(:modulepath).returns "eh" Puppet::Node::Environment.expects(:new).with("foo").returns env Puppet::Module.modulepath("foo").should == "eh" end end describe Puppet::Module, "when finding matching manifests" do before do @mod = Puppet::Module.new("mymod") @mod.stubs(:path).returns "/a" @pq_glob_with_extension = "yay/*.xx" @fq_glob_with_extension = "/a/manifests/#{@pq_glob_with_extension}" end it "should return all manifests matching the glob pattern" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.stubs(:directory?).returns false @mod.match_manifests(@pq_glob_with_extension).should == %w{foo bar} end it "should not return directories" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{foo bar}) FileTest.expects(:directory?).with("foo").returns false FileTest.expects(:directory?).with("bar").returns true @mod.match_manifests(@pq_glob_with_extension).should == %w{foo} end it "should default to the 'init' file if no glob pattern is specified" do Dir.expects(:glob).with("/a/manifests/init.{pp,rb}").returns(%w{/a/manifests/init.pp}) @mod.match_manifests(nil).should == %w{/a/manifests/init.pp} end it "should return all manifests matching the glob pattern in all existing paths" do Dir.expects(:glob).with(@fq_glob_with_extension).returns(%w{a b}) @mod.match_manifests(@pq_glob_with_extension).should == %w{a b} end it "should match the glob pattern plus '.{pp,rb}' if no extention is specified" do Dir.expects(:glob).with("/a/manifests/yay/foo.{pp,rb}").returns(%w{yay}) @mod.match_manifests("yay/foo").should == %w{yay} end it "should return an empty array if no manifests matched" do Dir.expects(:glob).with(@fq_glob_with_extension).returns([]) @mod.match_manifests(@pq_glob_with_extension).should == [] end end describe Puppet::Module do before do Puppet::Module.any_instance.stubs(:path).returns "/my/mod/path" @module = Puppet::Module.new("foo") end it "should use 'License' in its current path as its metadata file" do @module.license_file.should == "/my/mod/path/License" end it "should return nil as its license file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").license_file.should be_nil end it "should cache the license file" do Puppet::Module.any_instance.expects(:path).once.returns nil mod = Puppet::Module.new("foo") mod.license_file.should == mod.license_file end it "should use 'metadata.json' in its current path as its metadata file" do @module.metadata_file.should == "/my/mod/path/metadata.json" end it "should return nil as its metadata file when the module has no path" do Puppet::Module.any_instance.stubs(:path).returns nil Puppet::Module.new("foo").metadata_file.should be_nil end it "should cache the metadata file" do Puppet::Module.any_instance.expects(:path).once.returns nil mod = Puppet::Module.new("foo") mod.metadata_file.should == mod.metadata_file end it "should know if it has a metadata file" do FileTest.expects(:exist?).with(@module.metadata_file).returns true @module.should be_has_metadata end it "should know if it is missing a metadata file" do FileTest.expects(:exist?).with(@module.metadata_file).returns false @module.should_not be_has_metadata end it "should be able to parse its metadata file" do @module.should respond_to(:load_metadata) end it "should parse its metadata file on initialization if it is present" do Puppet::Module.any_instance.expects(:has_metadata?).returns true Puppet::Module.any_instance.expects(:load_metadata) Puppet::Module.new("yay") end describe "when loading the medatada file", :if => Puppet.features.json? do before do @data = { :license => "GPL2", :author => "luke", :version => "1.0", :source => "http://foo/", :puppetversion => "0.25" } @text = @data.to_json @module = Puppet::Module.new("foo") @module.stubs(:metadata_file).returns "/my/file" File.stubs(:read).with("/my/file").returns @text end %w{source author version license}.each do |attr| it "should set #{attr} if present in the metadata file" do @module.load_metadata @module.send(attr).should == @data[attr.to_sym] end it "should fail if #{attr} is not present in the metadata file" do @data.delete(attr.to_sym) @text = @data.to_json File.stubs(:read).with("/my/file").returns @text lambda { @module.load_metadata }.should raise_error(Puppet::Module::MissingMetadata) end end it "should set puppetversion if present in the metadata file" do @module.load_metadata @module.puppetversion.should == @data[:puppetversion] end it "should fail if the discovered name is different than the metadata name" end end diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb index c5d3e0470..32c7a90fe 100755 --- a/spec/unit/network/http_pool_spec.rb +++ b/spec/unit/network/http_pool_spec.rb @@ -1,206 +1,206 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2007-11-26. # Copyright (c) 2007. All rights reserved. require 'spec_helper' require 'puppet/network/http_pool' describe Puppet::Network::HttpPool do after do Puppet::Util::Cacher.expire Puppet::Network::HttpPool.clear_http_instances Puppet::Network::HttpPool.instance_variable_set("@ssl_host", nil) end it "should have keep-alive disabled" do Puppet::Network::HttpPool::HTTP_KEEP_ALIVE.should be_false end it "should use the global SSL::Host instance to get its certificate information" do host = mock 'host' Puppet::SSL::Host.expects(:localhost).with.returns host Puppet::Network::HttpPool.ssl_host.should equal(host) end describe "when managing http instances" do def stub_settings(settings) settings.each do |param, value| Puppet.settings.stubs(:value).with(param).returns(value) end end before do # All of the cert stuff is tested elsewhere Puppet::Network::HttpPool.stubs(:cert_setup) end it "should return an http instance created with the passed host and port" do http = stub 'http', :use_ssl= => nil, :read_timeout= => nil, :open_timeout= => nil, :started? => false Net::HTTP.expects(:new).with("me", 54321, nil, nil).returns(http) Puppet::Network::HttpPool.http_instance("me", 54321).should equal(http) end it "should enable ssl on the http instance" do Puppet::Network::HttpPool.http_instance("me", 54321).instance_variable_get("@use_ssl").should be_true end it "should set the read timeout" do Puppet::Network::HttpPool.http_instance("me", 54321).read_timeout.should == 120 end it "should set the open timeout" do Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 end it "should create the http instance with the proxy host and port set if the http_proxy is not set to 'none'" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 Puppet::Network::HttpPool.http_instance("me", 54321).open_timeout.should == 120 end describe "and http keep-alive is enabled" do before do Puppet::Network::HttpPool.stubs(:keep_alive?).returns true end it "should cache http instances" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) end it "should have a mechanism for getting a new http instance instead of the cached instance" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321, true).should_not equal(old) end it "should close existing, open connections when requesting a new connection" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) old.expects(:started?).returns(true) old.expects(:finish) Puppet::Network::HttpPool.http_instance("me", 54321, true) end - it "should have a mechanism for clearing the http cache" do + it "should have a mechanism for clearing the http cache", :fails_on_windows => true do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321).should equal(old) old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.clear_http_instances Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) end it "should close open http connections when clearing the cache" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 one = Puppet::Network::HttpPool.http_instance("me", 54321) one.expects(:started?).returns(true) one.expects(:finish).returns(true) Puppet::Network::HttpPool.clear_http_instances end it "should not close unopened http connections when clearing the cache" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 one = Puppet::Network::HttpPool.http_instance("me", 54321) one.expects(:started?).returns(false) one.expects(:finish).never Puppet::Network::HttpPool.clear_http_instances end end describe "and http keep-alive is disabled" do before do Puppet::Network::HttpPool.stubs(:keep_alive?).returns false end it "should not cache http instances" do stub_settings :http_proxy_host => "myhost", :http_proxy_port => 432, :configtimeout => 120 old = Puppet::Network::HttpPool.http_instance("me", 54321) Puppet::Network::HttpPool.http_instance("me", 54321).should_not equal(old) end end after do Puppet::Network::HttpPool.clear_http_instances end end describe "when adding certificate information to http instances" do before do @http = mock 'http' [:cert_store=, :verify_mode=, :ca_file=, :cert=, :key=].each { |m| @http.stubs(m) } @store = stub 'store' @cert = stub 'cert', :content => "real_cert" @key = stub 'key', :content => "real_key" @host = stub 'host', :certificate => @cert, :key => @key, :ssl_store => @store Puppet[:confdir] = "/sometthing/else" Puppet.settings.stubs(:value).returns "/some/file" Puppet.settings.stubs(:value).with(:hostcert).returns "/host/cert" Puppet.settings.stubs(:value).with(:localcacert).returns "/local/ca/cert" FileTest.stubs(:exist?).with("/host/cert").returns true FileTest.stubs(:exist?).with("/local/ca/cert").returns true Puppet::Network::HttpPool.stubs(:ssl_host).returns @host end after do Puppet.settings.clear end it "should do nothing if no host certificate is on disk" do FileTest.expects(:exist?).with("/host/cert").returns false @http.expects(:cert=).never Puppet::Network::HttpPool.cert_setup(@http) end it "should do nothing if no local certificate is on disk" do FileTest.expects(:exist?).with("/local/ca/cert").returns false @http.expects(:cert=).never Puppet::Network::HttpPool.cert_setup(@http) end it "should add a certificate store from the ssl host" do @http.expects(:cert_store=).with(@store) Puppet::Network::HttpPool.cert_setup(@http) end it "should add the client certificate" do @http.expects(:cert=).with("real_cert") Puppet::Network::HttpPool.cert_setup(@http) end it "should add the client key" do @http.expects(:key=).with("real_key") Puppet::Network::HttpPool.cert_setup(@http) end it "should set the verify mode to OpenSSL::SSL::VERIFY_PEER" do @http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER) Puppet::Network::HttpPool.cert_setup(@http) end it "should set the ca file" do Puppet.settings.stubs(:value).returns "/some/file" FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns true Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" FileTest.stubs(:exist?).with("/ca/cert/file").returns true @http.expects(:ca_file=).with("/ca/cert/file") Puppet::Network::HttpPool.cert_setup(@http) end it "should set up certificate information when creating http instances" do Puppet::Network::HttpPool.expects(:cert_setup).with { |i| i.is_a?(Net::HTTP) } Puppet::Network::HttpPool.http_instance("one", "two") end end end diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb index d1badfa3a..144e82e0c 100755 --- a/spec/unit/node/environment_spec.rb +++ b/spec/unit/node/environment_spec.rb @@ -1,334 +1,339 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/node/environment' require 'puppet/util/execution' describe Puppet::Node::Environment do include PuppetSpec::Files after do Puppet::Node::Environment.clear end it "should include the Cacher module" do Puppet::Node::Environment.ancestors.should be_include(Puppet::Util::Cacher) end it "should use the filetimeout for the ttl for the modulepath" do Puppet::Node::Environment.attr_ttl(:modulepath).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the module list" do Puppet::Node::Environment.attr_ttl(:modules).should == Integer(Puppet[:filetimeout]) end it "should use the filetimeout for the ttl for the manifestdir" do Puppet::Node::Environment.attr_ttl(:manifestdir).should == Integer(Puppet[:filetimeout]) end it "should use the default environment if no name is provided while initializing an environment" do Puppet.settings.expects(:value).with(:environment).returns("one") Puppet::Node::Environment.new.name.should == :one end it "should treat environment instances as singletons" do Puppet::Node::Environment.new("one").should equal(Puppet::Node::Environment.new("one")) end it "should treat an environment specified as names or strings as equivalent" do Puppet::Node::Environment.new(:one).should equal(Puppet::Node::Environment.new("one")) end it "should return its name when converted to a string" do Puppet::Node::Environment.new(:one).to_s.should == "one" end it "should just return any provided environment if an environment is provided as the name" do one = Puppet::Node::Environment.new(:one) Puppet::Node::Environment.new(one).should equal(one) end describe "when managing known resource types" do before do @env = Puppet::Node::Environment.new("dev") @collection = Puppet::Resource::TypeCollection.new(@env) @env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) Thread.current[:known_resource_types] = nil end it "should create a resource type collection if none exists" do Puppet::Resource::TypeCollection.expects(:new).with(@env).returns @collection @env.known_resource_types.should equal(@collection) end it "should reuse any existing resource type collection" do @env.known_resource_types.should equal(@env.known_resource_types) end it "should perform the initial import when creating a new collection" do @env = Puppet::Node::Environment.new("dev") @env.expects(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new('')) @env.known_resource_types end it "should return the same collection even if stale if it's the same thread" do Puppet::Resource::TypeCollection.stubs(:new).returns @collection @env.known_resource_types.stubs(:stale?).returns true @env.known_resource_types.should equal(@collection) end it "should return the current thread associated collection if there is one" do Thread.current[:known_resource_types] = @collection @env.known_resource_types.should equal(@collection) end it "should give to all threads using the same environment the same collection if the collection isn't stale" do original_thread_type_collection = Puppet::Resource::TypeCollection.new(@env) Puppet::Resource::TypeCollection.expects(:new).with(@env).returns original_thread_type_collection @env.known_resource_types.should equal(original_thread_type_collection) original_thread_type_collection.expects(:require_reparse?).returns(false) Puppet::Resource::TypeCollection.stubs(:new).with(@env).returns @collection t = Thread.new { @env.known_resource_types.should equal(original_thread_type_collection) } t.join end it "should generate a new TypeCollection if the current one requires reparsing" do old_type_collection = @env.known_resource_types old_type_collection.stubs(:require_reparse?).returns true Thread.current[:known_resource_types] = nil new_type_collection = @env.known_resource_types new_type_collection.should be_a Puppet::Resource::TypeCollection new_type_collection.should_not equal(old_type_collection) end end [:modulepath, :manifestdir].each do |setting| it "should validate the #{setting} directories" do path = %w{/one /two}.join(File::PATH_SEPARATOR) env = Puppet::Node::Environment.new("testing") env.stubs(:[]).with(setting).returns path env.expects(:validate_dirs).with(%w{/one /two}) env.send(setting) end it "should return the validated dirs for #{setting}" do path = %w{/one /two}.join(File::PATH_SEPARATOR) env = Puppet::Node::Environment.new("testing") env.stubs(:[]).with(setting).returns path env.stubs(:validate_dirs).returns %w{/one /two} env.send(setting).should == %w{/one /two} end end it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do Puppet::Util::Execution.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do env = Puppet::Node::Environment.new("testing") module_path = %w{/one /two}.join(File::PATH_SEPARATOR) env.expects(:validate_dirs).with(%w{/l1 /l2 /one /two}).returns %w{/l1 /l2 /one /two} env.expects(:[]).with(:modulepath).returns module_path env.modulepath.should == %w{/l1 /l2 /one /two} end end describe "when validating modulepath or manifestdir directories" do + before :each do + @path_one = make_absolute('/one') + @path_two = make_absolute('/two') + end + it "should not return non-directories" do env = Puppet::Node::Environment.new("testing") - FileTest.expects(:directory?).with("/one").returns true - FileTest.expects(:directory?).with("/two").returns false + FileTest.expects(:directory?).with(@path_one).returns true + FileTest.expects(:directory?).with(@path_two).returns false - env.validate_dirs(%w{/one /two}).should == %w{/one} + env.validate_dirs([@path_one, @path_two]).should == [@path_one] end it "should use the current working directory to fully-qualify unqualified paths" do FileTest.stubs(:directory?).returns true env = Puppet::Node::Environment.new("testing") two = File.join(Dir.getwd, "two") - env.validate_dirs(%w{/one two}).should == ["/one", two] + env.validate_dirs([@path_one, 'two']).should == [@path_one, two] end end describe "when modeling a specific environment" do it "should have a method for returning the environment name" do Puppet::Node::Environment.new("testing").name.should == :testing end it "should provide an array-like accessor method for returning any environment-specific setting" do env = Puppet::Node::Environment.new("testing") env.should respond_to(:[]) end it "should ask the Puppet settings instance for the setting qualified with the environment name" do Puppet.settings.expects(:value).with("myvar", :testing).returns("myval") env = Puppet::Node::Environment.new("testing") env["myvar"].should == "myval" end it "should be able to return an individual module that exists in its module path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns true env.module("one").should equal(mod) end it "should return nil if asked for a module that does not exist in its path" do env = Puppet::Node::Environment.new("testing") mod = mock 'module' Puppet::Module.expects(:new).with("one", env).returns mod mod.expects(:exist?).returns false env.module("one").should be_nil end it "should be able to return its modules" do Puppet::Node::Environment.new("testing").should respond_to(:modules) end describe ".modules" do it "should return a module named for every directory in each module path" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a /b} Dir.expects(:entries).with("/a").returns %w{foo bar} Dir.expects(:entries).with("/b").returns %w{bee baz} env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort end it "should remove duplicates" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).returns( %w{/a /b} ).at_least_once Dir.expects(:entries).with("/a").returns %w{foo} Dir.expects(:entries).with("/b").returns %w{foo} env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should ignore invalid modules" do env = Puppet::Node::Environment.new("testing") env.stubs(:modulepath).returns %w{/a} Dir.expects(:entries).with("/a").returns %w{foo bar} Puppet::Module.expects(:new).with { |name, env| name == "foo" }.returns mock("foomod", :name => "foo") Puppet::Module.expects(:new).with { |name, env| name == "bar" }.raises( Puppet::Module::InvalidName, "name is invalid" ) env.modules.collect{|mod| mod.name}.sort.should == %w{foo} end it "should create modules with the correct environment" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).with("/a").returns %w{foo} env.modules.each {|mod| mod.environment.should == env } end it "should cache the module list" do env = Puppet::Node::Environment.new("testing") env.expects(:modulepath).at_least_once.returns %w{/a} Dir.expects(:entries).once.with("/a").returns %w{foo} env.modules env.modules end end end describe Puppet::Node::Environment::Helper do before do @helper = Object.new @helper.extend(Puppet::Node::Environment::Helper) end it "should be able to set and retrieve the environment" do @helper.environment = :foo @helper.environment.name.should == :foo end it "should accept an environment directly" do env = Puppet::Node::Environment.new :foo @helper.environment = env @helper.environment.name.should == :foo end it "should accept an environment as a string" do env = Puppet::Node::Environment.new "foo" @helper.environment = env @helper.environment.name.should == :foo end end describe "when performing initial import" do before do @parser = Puppet::Parser::Parser.new("test") Puppet::Parser::Parser.stubs(:new).returns @parser @env = Puppet::Node::Environment.new("env") end it "should set the parser's string to the 'code' setting and parse if code is available" do Puppet.settings[:code] = "my code" @parser.expects(:string=).with "my code" @parser.expects(:parse) @env.instance_eval { perform_initial_import } end it "should set the parser's file to the 'manifest' setting and parse if no code is available and the manifest is available" do filename = tmpfile('myfile') File.open(filename, 'w'){|f| } Puppet.settings[:manifest] = filename @parser.expects(:file=).with filename @parser.expects(:parse) @env.instance_eval { perform_initial_import } end it "should pass the manifest file to the parser even if it does not exist on disk" do filename = tmpfile('myfile') Puppet.settings[:code] = "" Puppet.settings[:manifest] = filename @parser.expects(:file=).with(filename).once @parser.expects(:parse).once @env.instance_eval { perform_initial_import } end it "should fail helpfully if there is an error importing" do File.stubs(:exist?).returns true @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) @parser.expects(:file=).once @parser.expects(:parse).raises ArgumentError lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error) end it "should not do anything if the ignore_import settings is set" do Puppet.settings[:ignoreimport] = true @parser.expects(:string=).never @parser.expects(:file=).never @parser.expects(:parse).never @env.instance_eval { perform_initial_import } end it "should mark the type collection as needing a reparse when there is an error parsing" do @parser.expects(:parse).raises Puppet::ParseError.new("Syntax error at ...") @env.stubs(:known_resource_types).returns Puppet::Resource::TypeCollection.new(@env) lambda { @env.instance_eval { perform_initial_import } }.should raise_error(Puppet::Error, /Syntax error at .../) @env.known_resource_types.require_reparse?.should be_true end end end diff --git a/spec/unit/other/selinux_spec.rb b/spec/unit/other/selinux_spec.rb index 216feaf1f..f20951868 100755 --- a/spec/unit/other/selinux_spec.rb +++ b/spec/unit/other/selinux_spec.rb @@ -1,93 +1,95 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/type/selboolean' require 'puppet/type/selmodule' describe Puppet::Type.type(:file), " when manipulating file contexts" do + include PuppetSpec::Files + before :each do @file = Puppet::Type::File.new( - :name => "/tmp/foo", + :name => make_absolute("/tmp/foo"), :ensure => "file", :seluser => "user_u", :selrole => "role_r", :seltype => "type_t" ) end it "should use :seluser to get/set an SELinux user file context attribute" do @file.property(:seluser).should == "user_u" end it "should use :selrole to get/set an SELinux role file context attribute" do @file.property(:selrole).should == "role_r" end it "should use :seltype to get/set an SELinux user file context attribute" do @file.property(:seltype).should == "type_t" end end describe Puppet::Type.type(:selboolean), " when manipulating booleans" do before :each do provider_class = Puppet::Type::Selboolean.provider(Puppet::Type::Selboolean.providers[0]) Puppet::Type::Selboolean.stubs(:defaultprovider).returns provider_class @bool = Puppet::Type::Selboolean.new( :name => "foo", :value => "on", :persistent => true ) end it "should be able to access :name" do @bool[:name].should == "foo" end it "should be able to access :value" do @bool.property(:value).should == :on end it "should set :value to off" do @bool[:value] = :off @bool.property(:value).should == :off end it "should be able to access :persistent" do @bool[:persistent].should == :true end it "should set :persistent to false" do @bool[:persistent] = false @bool[:persistent].should == :false end end describe Puppet::Type.type(:selmodule), " when checking policy modules" do before :each do provider_class = Puppet::Type::Selmodule.provider(Puppet::Type::Selmodule.providers[0]) Puppet::Type::Selmodule.stubs(:defaultprovider).returns provider_class @module = Puppet::Type::Selmodule.new( :name => "foo", :selmoduledir => "/some/path", :selmodulepath => "/some/path/foo.pp", :syncversion => true) end it "should be able to access :name" do @module[:name].should == "foo" end it "should be able to access :selmoduledir" do @module[:selmoduledir].should == "/some/path" end it "should be able to access :selmodulepath" do @module[:selmodulepath].should == "/some/path/foo.pp" end it "should be able to access :syncversion" do @module.property(:syncversion).should == :true end it "should set the syncversion value to false" do @module[:syncversion] = :false @module.property(:syncversion).should == :false end end diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb index e6f481114..411d1b862 100755 --- a/spec/unit/parser/compiler_spec.rb +++ b/spec/unit/parser/compiler_spec.rb @@ -1,820 +1,822 @@ #!/usr/bin/env rspec require 'spec_helper' class CompilerTestResource attr_accessor :builtin, :virtual, :evaluated, :type, :title def initialize(type, title) @type = type @title = title end def [](attr) return nil if attr == :stage :main end def ref "#{type.to_s.capitalize}[#{title}]" end def evaluated? @evaluated end def builtin_type? @builtin end def virtual? @virtual end def evaluate end def file "/fake/file/goes/here" end def line "42" end end describe Puppet::Parser::Compiler do + include PuppetSpec::Files + def resource(type, title) Puppet::Parser::Resource.new(type, title, :scope => @scope) end before :each do # Push me faster, I wanna go back in time! (Specifically, freeze time # across the test since we have a bunch of version == timestamp code # hidden away in the implementation and we keep losing the race.) # --daniel 2011-04-21 now = Time.now Time.stubs(:now).returns(now) @node = Puppet::Node.new "testnode" @known_resource_types = Puppet::Resource::TypeCollection.new "development" @compiler = Puppet::Parser::Compiler.new(@node) @scope = Puppet::Parser::Scope.new(:compiler => @compiler, :source => stub('source')) @scope_resource = Puppet::Parser::Resource.new(:file, "/my/file", :scope => @scope) @scope.resource = @scope_resource @compiler.environment.stubs(:known_resource_types).returns @known_resource_types end it "should have a class method that compiles, converts, and returns a catalog" do compiler = stub 'compiler' Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler catalog = stub 'catalog' compiler.expects(:compile).returns catalog converted_catalog = stub 'converted_catalog' catalog.expects(:to_resource).returns converted_catalog Puppet::Parser::Compiler.compile(@node).should equal(converted_catalog) end it "should fail intelligently when a class-level compile fails" do Puppet::Parser::Compiler.expects(:new).raises ArgumentError lambda { Puppet::Parser::Compiler.compile(@node) }.should raise_error(Puppet::Error) end it "should use the node's environment as its environment" do @compiler.environment.should equal(@node.environment) end it "should include the resource type collection helper" do Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Resource::TypeCollectionHelper) end it "should be able to return a class list containing all added classes" do @compiler.add_class "" @compiler.add_class "one" @compiler.add_class "two" @compiler.classlist.sort.should == %w{one two}.sort end describe "when initializing" do it "should set its node attribute" do @compiler.node.should equal(@node) end it "should detect when ast nodes are absent" do @compiler.ast_nodes?.should be_false end it "should detect when ast nodes are present" do @known_resource_types.expects(:nodes?).returns true @compiler.ast_nodes?.should be_true end it "should copy the known_resource_types version to the catalog" do @compiler.catalog.version.should == @known_resource_types.version end it "should copy any node classes into the class list" do node = Puppet::Node.new("mynode") node.classes = %w{foo bar} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should =~ ['foo', 'bar'] end it "should transform node class hashes into a class list" do node = Puppet::Node.new("mynode") node.classes = {'foo'=>{'one'=>'1'}, 'bar'=>{'two'=>'2'}} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should =~ ['foo', 'bar'] end it "should add a 'main' stage to the catalog" do @compiler.catalog.resource(:stage, :main).should be_instance_of(Puppet::Parser::Resource) end end describe "when managing scopes" do it "should create a top scope" do @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) end it "should be able to create new scopes" do @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) end it "should set the parent scope of the new scope to be the passed-in parent" do scope = mock 'scope' newscope = @compiler.newscope(scope) newscope.parent.should equal(scope) end it "should set the parent scope of the new scope to its topscope if the parent passed in is nil" do scope = mock 'scope' newscope = @compiler.newscope(nil) newscope.parent.should equal(@compiler.topscope) end end describe "when compiling" do def compile_methods [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, :finish, :store, :extract, :evaluate_relationships] end # Stub all of the main compile methods except the ones we're specifically interested in. def compile_stub(*except) (compile_methods - except).each { |m| @compiler.stubs(m) } end it "should set node parameters as variables in the top scope" do params = {"a" => "b", "c" => "d"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.topscope['a'].should == "b" @compiler.topscope['c'].should == "d" end it "should set the client and server versions on the catalog" do params = {"clientversion" => "2", "serverversion" => "3"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.catalog.client_version.should == "2" @compiler.catalog.server_version.should == "3" end it "should evaluate any existing classes named in the node" do classes = %w{one two three four} main = stub 'main' one = stub 'one', :name => "one" three = stub 'three', :name => "three" @node.stubs(:name).returns("whatever") @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } end it "should evaluate any parameterized classes named in the node" do classes = {'foo'=>{'1'=>'one'}, 'bar'=>{'2'=>'two'}} @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.compile end it "should evaluate the main class if it exists" do compile_stub(:evaluate_main) main_class = @known_resource_types.add Puppet::Resource::Type.new(:hostclass, "") main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } @compiler.topscope.expects(:source=).with(main_class) @compiler.compile end it "should create a new, empty 'main' if no main class exists" do compile_stub(:evaluate_main) @compiler.compile @known_resource_types.find_hostclass([""], "").should be_instance_of(Puppet::Resource::Type) end it "should add an edge between the main stage and main class" do @compiler.compile (stage = @compiler.catalog.resource(:stage, "main")).should be_instance_of(Puppet::Parser::Resource) (klass = @compiler.catalog.resource(:class, "")).should be_instance_of(Puppet::Parser::Resource) @compiler.catalog.edge?(stage, klass).should be_true end it "should evaluate any node classes" do @node.stubs(:classes).returns(%w{one two three four}) @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) @compiler.send(:evaluate_node_classes) end it "should evaluate all added collections" do colls = [] # And when the collections fail to evaluate. colls << mock("coll1-false") colls << mock("coll2-false") colls.each { |c| c.expects(:evaluate).returns(false) } @compiler.add_collection(colls[0]) @compiler.add_collection(colls[1]) compile_stub(:evaluate_generators) @compiler.compile end it "should ignore builtin resources" do resource = resource(:file, "testing") @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true } @compiler.compile end it "should not evaluate already-evaluated resources" do resource = resource(:file, "testing") resource.stubs(:evaluated?).returns true @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources created by evaluating other resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) resource2 = CompilerTestResource.new(:file, "other") # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true; @compiler.add_resource(@scope, resource2) } resource2.expects(:evaluate).with { |*whatever| resource2.evaluated = true } @compiler.compile end describe "when finishing" do before do @compiler.send(:evaluate_main) @catalog = @compiler.catalog end def add_resource(name, parent = nil) resource = Puppet::Parser::Resource.new "file", name, :scope => @scope @compiler.add_resource(@scope, resource) @catalog.add_edge(parent, resource) if parent resource end it "should call finish() on all resources" do # Add a resource that does respond to :finish resource = Puppet::Parser::Resource.new "file", "finish", :scope => @scope resource.expects(:finish) @compiler.add_resource(@scope, resource) # And one that does not dnf_resource = stub_everything "dnf", :ref => "File[dnf]", :type => "file" @compiler.add_resource(@scope, dnf_resource) @compiler.send(:finish) end it "should call finish() in add_resource order" do resources = sequence('resources') resource1 = add_resource("finish1") resource1.expects(:finish).in_sequence(resources) resource2 = add_resource("finish2") resource2.expects(:finish).in_sequence(resources) @compiler.send(:finish) end it "should add each container's metaparams to its contained resources" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) @compiler.send(:finish) resource1[:noop].should be_true end it "should add metaparams recursively" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end it "should prefer metaparams from immediate parents" do main = @catalog.resource(:class, :main) main[:noop] = true resource1 = add_resource("meh", main) resource2 = add_resource("foo", resource1) resource1[:noop] = false @compiler.send(:finish) resource2[:noop].should be_false end it "should merge tags downward" do main = @catalog.resource(:class, :main) main.tag("one") resource1 = add_resource("meh", main) resource1.tag "two" resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2.tags.should be_include("one") resource2.tags.should be_include("two") end it "should work if only middle resources have metaparams set" do main = @catalog.resource(:class, :main) resource1 = add_resource("meh", main) resource1[:noop] = true resource2 = add_resource("foo", resource1) @compiler.send(:finish) resource2[:noop].should be_true end end it "should return added resources in add order" do resource1 = resource(:file, "yay") @compiler.add_resource(@scope, resource1) resource2 = resource(:file, "youpi") @compiler.add_resource(@scope, resource2) @compiler.resources.should == [resource1, resource2] end it "should add resources that do not conflict with existing resources" do resource = resource(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_vertex(resource) end it "should fail to add resources that conflict with existing resources" do - path = Puppet.features.posix? ? "/foo" : "C:/foo" + path = make_absolute("/foo") file1 = Puppet::Type.type(:file).new :path => path file2 = Puppet::Type.type(:file).new :path => path @compiler.add_resource(@scope, file1) lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should add an edge from the scope resource to the added resource" do resource = resource(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should not add non-class resources that don't specify a stage to the 'main' stage" do main = @compiler.catalog.resource(:stage, :main) resource = resource(:file, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should_not be_edge(main, resource) end it "should not add any parent-edges to stages" do stage = resource(:stage, "other") @compiler.add_resource(@scope, stage) @scope.resource = resource(:class, "foo") @compiler.catalog.edge?(@scope.resource, stage).should be_false end it "should not attempt to add stages to other stages" do other_stage = resource(:stage, "other") second_stage = resource(:stage, "second") @compiler.add_resource(@scope, other_stage) @compiler.add_resource(@scope, second_stage) second_stage[:stage] = "other" @compiler.catalog.edge?(other_stage, second_stage).should be_false end it "should have a method for looking up resources" do resource = resource(:yay, "foo") @compiler.add_resource(@scope, resource) @compiler.findresource("Yay[foo]").should equal(resource) end it "should be able to look resources up by type and title" do resource = resource(:yay, "foo") @compiler.add_resource(@scope, resource) @compiler.findresource("Yay", "foo").should equal(resource) end it "should not evaluate virtual defined resources" do resource = resource(:file, "testing") resource.virtual = true @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end end describe "when evaluating collections" do it "should evaluate each collection" do 2.times { |i| coll = mock 'coll%s' % i @compiler.add_collection(coll) # This is the hard part -- we have to emulate the fact that # collections delete themselves if they are done evaluating. coll.expects(:evaluate).with do @compiler.delete_collection(coll) end } @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } end it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(nil) @compiler.add_collection(coll) lambda { @compiler.compile }.should_not raise_error end it "should fail when there are unevaluated resource collections that refer to a specific resource" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(:something) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources something' end it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns([:one, :two]) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Failed to realize virtual resources one, two' end end describe "when evaluating relationships" do it "should evaluate each relationship with its catalog" do dep = stub 'dep' dep.expects(:evaluate).with(@compiler.catalog) @compiler.add_relationship dep @compiler.evaluate_relationships end end describe "when told to evaluate missing classes" do it "should fail if there's no source listed for the scope" do scope = stub 'scope', :source => nil proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) end it "should raise an error if a class is not found" do @scope.expects(:find_hostclass).with("notfound").returns(nil) lambda{ @compiler.evaluate_classes(%w{notfound}, @scope) }.should raise_error(Puppet::Error, /Could not find class/) end it "should raise an error when it can't find class" do klasses = {'foo'=>nil} @node.classes = klasses @compiler.topscope.stubs(:find_hostclass).with('foo').returns(nil) lambda{ @compiler.compile }.should raise_error(Puppet::Error, /Could not find class foo for testnode/) end end describe "when evaluating found classes" do before do @class = stub 'class', :name => "my::class" @scope.stubs(:find_hostclass).with("myclass").returns(@class) @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" end it "should evaluate each class" do @compiler.catalog.stubs(:tag) @class.expects(:ensure_in_catalog).with(@scope) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end describe "and the classes are specified as a hash with parameters" do before do @node.classes = {} @ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') end # Define the given class with default parameters def define_class(name, parameters) @node.classes[name] = parameters klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => @ast_obj, '2' => @ast_obj}) @compiler.topscope.known_resource_types.add klass end def compile @catalog = @compiler.compile end it "should record which classes are evaluated" do classes = {'foo'=>{}, 'bar::foo'=>{}, 'bar'=>{}} classes.each { |c, params| define_class(c, params) } compile() classes.each { |name, p| @catalog.classes.should include(name) } end it "should provide default values for parameters that have no values specified" do define_class('foo', {}) compile() @catalog.resource(:class, 'foo')['1'].should == "foo" end it "should use any provided values" do define_class('foo', {'1' => 'real_value'}) compile() @catalog.resource(:class, 'foo')['1'].should == "real_value" end it "should support providing some but not all values" do define_class('foo', {'1' => 'real_value'}) compile() @catalog.resource(:class, 'Foo')['1'].should == "real_value" @catalog.resource(:class, 'Foo')['2'].should == "foo" end it "should ensure each node class is in catalog and has appropriate tags", :'fails_on_ruby_1.9.2' => true do klasses = ['bar::foo'] @node.classes = klasses ast_obj = Puppet::Parser::AST::String.new(:value => 'foo') klasses.each do |name| klass = Puppet::Resource::Type.new(:hostclass, name, :arguments => {'1' => ast_obj, '2' => ast_obj}) @compiler.topscope.known_resource_types.add klass end catalog = @compiler.compile r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' } r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo'] end end it "should fail if required parameters are missing" do klass = {'foo'=>{'1'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {'1' => nil, '2' => nil}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Must pass 2 to Class[Foo]") end it "should fail if invalid parameters are passed" do klass = {'foo'=>{'3'=>'one'}} @node.classes = klass klass = Puppet::Resource::Type.new(:hostclass, 'foo', :arguments => {}) @compiler.topscope.known_resource_types.add klass lambda { @compiler.compile }.should raise_error(Puppet::ParseError, "Invalid parameter 3") end it "should ensure class is in catalog without params" do @node.classes = klasses = {'foo'=>nil} foo = Puppet::Resource::Type.new(:hostclass, 'foo') @compiler.topscope.known_resource_types.add foo catalog = @compiler.compile catalog.classes.should include 'foo' end it "should not evaluate the resources created for found classes unless asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate).never @class.expects(:ensure_in_catalog).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should immediately evaluate the resources created for found classes when asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate) @class.expects(:ensure_in_catalog).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes that have already been evaluated" do @compiler.catalog.stubs(:tag) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes previously evaluated with different capitalization" do @compiler.catalog.stubs(:tag) @scope.stubs(:find_hostclass).with("MyClass").returns(@class) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{MyClass}, @scope, false) end end describe "when evaluating AST nodes with no AST nodes present" do it "should do nothing" do @compiler.expects(:ast_nodes?).returns(false) @compiler.known_resource_types.expects(:nodes).never Puppet::Parser::Resource.expects(:new).never @compiler.send(:evaluate_ast_node) end end describe "when evaluating AST nodes with AST nodes present" do before do @compiler.known_resource_types.stubs(:nodes?).returns true # Set some names for our test @node.stubs(:names).returns(%w{a b c}) @compiler.known_resource_types.stubs(:node).with("a").returns(nil) @compiler.known_resource_types.stubs(:node).with("b").returns(nil) @compiler.known_resource_types.stubs(:node).with("c").returns(nil) # It should check this last, of course. @compiler.known_resource_types.stubs(:node).with("default").returns(nil) end it "should fail if the named node cannot be found" do proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) end it "should evaluate the first node class matching the node name" do node_class = stub 'node', :name => "c", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) @compiler.compile end it "should match the default node if no matching node can be found" do node_class = stub 'node', :name => "default", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("default").returns(node_class) node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) @compiler.compile end it "should evaluate the node resource immediately rather than using lazy evaluation" do node_class = stub 'node', :name => "c" @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :type => "node" node_class.expects(:ensure_in_catalog).returns(node_resource) node_resource.expects(:evaluate) @compiler.send(:evaluate_ast_node) end it "should set the node's scope as the top scope" do node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class = stub 'node', :name => "c", :ensure_in_catalog => node_resource @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) # The #evaluate method normally does this. scope = stub 'scope', :source => "mysource" @compiler.topscope.expects(:class_scope).with(node_class).returns(scope) node_resource.stubs(:evaluate) @compiler.stubs :create_settings_scope @compiler.compile @compiler.topscope.should equal(scope) end end describe "when managing resource overrides" do before do @override = stub 'override', :ref => "File[/foo]", :type => "my" @resource = resource(:file, "/foo") end it "should be able to store overrides" do lambda { @compiler.add_override(@override) }.should_not raise_error end it "should apply overrides to the appropriate resources" do @compiler.add_resource(@scope, @resource) @resource.expects(:merge).with(@override) @compiler.add_override(@override) @compiler.compile end it "should accept overrides before the related resource has been created" do @resource.expects(:merge).with(@override) # First store the override @compiler.add_override(@override) # Then the resource @compiler.add_resource(@scope, @resource) # And compile, so they get resolved @compiler.compile end it "should fail if the compile is finished and resource overrides have not been applied" do @compiler.add_override(@override) lambda { @compiler.compile }.should raise_error Puppet::ParseError, 'Could not find resource(s) File[/foo] for overriding' end end end diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb index 04777f0ec..1bf75e623 100755 --- a/spec/unit/parser/files_spec.rb +++ b/spec/unit/parser/files_spec.rb @@ -1,200 +1,203 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/parser/files' describe Puppet::Parser::Files do + include PuppetSpec::Files before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + @basepath = make_absolute("/somepath") end it "should have a method for finding a template" do Puppet::Parser::Files.should respond_to(:find_template) end it "should have a method for finding manifests" do Puppet::Parser::Files.should respond_to(:find_manifests) end describe "when searching for templates" do it "should return fully-qualified templates directly" do Puppet::Parser::Files.expects(:modulepath).never Puppet::Parser::Files.find_template(@basepath + "/my/template").should == @basepath + "/my/template" end it "should return the template from the first found module" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/one/mymod/templates/mytemplate") Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" end it "should return the file in the templatedir if it exists" do Puppet.settings.expects(:value).with(:templatedir, nil).returns("/my/templates") Puppet[:modulepath] = "/one:/two" File.stubs(:directory?).returns(true) FileTest.stubs(:exist?).returns(true) Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate" end it "should not raise an error if no valid templatedir exists and the template exists in a module" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:template).returns("/one/mymod/templates/mytemplate") Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(nil) Puppet::Parser::Files.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate" end it "should return unqualified templates if they exist in the template dir" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should only return templates if they actually exist" do FileTest.expects(:exist?).with("/my/templates/mytemplate").returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should == "/my/templates/mytemplate" end it "should return nil when asked for a template that doesn't exist" do FileTest.expects(:exist?).with("/my/templates/mytemplate").returns false Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Parser::Files.find_template("mytemplate").should be_nil end it "should search in the template directories before modules" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with(nil).returns(["/my/templates"]) Puppet::Module.expects(:find).never Puppet::Parser::Files.find_template("mytemplate") end it "should accept relative templatedirs" do FileTest.stubs(:exist?).returns true Puppet[:templatedir] = "my/templates" - File.expects(:directory?).with(File.join(Dir.getwd,"my/templates")).returns(true) - Puppet::Parser::Files.find_template("mytemplate").should == File.join(Dir.getwd,"my/templates/mytemplate") + # We expand_path to normalize backslashes and slashes on Windows + File.expects(:directory?).with(File.expand_path(File.join(Dir.getwd,"my/templates"))).returns(true) + Puppet::Parser::Files.find_template("mytemplate").should == File.expand_path(File.join(Dir.getwd,"my/templates/mytemplate")) end it "should use the environment templatedir if no module is found and an environment is specified" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use first dir from environment templatedir if no module is found and an environment is specified" do FileTest.stubs(:exist?).returns true Puppet::Parser::Files.stubs(:templatepath).with("myenv").returns(["/myenv/templates", "/two/templates"]) Puppet::Parser::Files.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and the first dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/one/templates/mytemplate" end it "should use a valid dir when templatedir is a path for unqualified templates and only second dir contains template" do Puppet::Parser::Files.stubs(:templatepath).returns(["/one/templates", "/two/templates"]) FileTest.expects(:exist?).with("/one/templates/mytemplate").returns(false) FileTest.expects(:exist?).with("/two/templates/mytemplate").returns(true) Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate" end it "should use the node environment if specified" do mod = mock 'module' Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod mod.expects(:template).returns("/my/modules/mymod/templates/envtemplate") Puppet::Parser::Files.find_template("mymod/envtemplate", "myenv").should == "/my/modules/mymod/templates/envtemplate" end it "should return nil if no template can be found" do Puppet::Parser::Files.find_template("foomod/envtemplate", "myenv").should be_nil end after { Puppet.settings.clear } end describe "when searching for manifests" do it "should ignore invalid modules" do mod = mock 'module' Puppet::Node::Environment.new.expects(:module).with("mymod").raises(Puppet::Module::InvalidName, "name is invalid") Puppet.expects(:value).with(:modulepath).never Dir.stubs(:glob).returns %w{foo} Puppet::Parser::Files.find_manifests("mymod/init.pp") end end describe "when searching for manifests when no module is found" do before do File.stubs(:find).returns(nil) end it "should not look for modules when paths are fully qualified" do Puppet.expects(:value).with(:modulepath).never file = @basepath + "/fully/qualified/file.pp" Dir.stubs(:glob).with(file).returns([file]) Puppet::Parser::Files.find_manifests(file) end it "should return nil and an array of fully qualified files" do file = @basepath + "/fully/qualified/file.pp" Dir.stubs(:glob).with(file).returns([file]) Puppet::Parser::Files.find_manifests(file).should == [nil, [file]] end it "should match against provided fully qualified patterns" do pattern = @basepath + "/fully/qualified/pattern/*" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns(%w{my file list}) Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{my file list} end it "should look for files relative to the current directory" do - cwd = Dir.getwd + # We expand_path to normalize backslashes and slashes on Windows + cwd = File.expand_path(Dir.getwd) Dir.expects(:glob).with("#{cwd}/foobar/init.pp").returns(["#{cwd}/foobar/init.pp"]) Puppet::Parser::Files.find_manifests("foobar/init.pp")[1].should == ["#{cwd}/foobar/init.pp"] end it "should only return files, not directories" do pattern = @basepath + "/fully/qualified/pattern/*" file = @basepath + "/my/file" dir = @basepath + "/my/directory" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns([file, dir]) FileTest.expects(:directory?).with(file).returns(false) FileTest.expects(:directory?).with(dir).returns(true) Puppet::Parser::Files.find_manifests(pattern)[1].should == [file] end it "should return files once only" do pattern = @basepath + "/fully/qualified/pattern/*" Dir.expects(:glob).with(pattern+'{.pp,.rb}').returns(%w{one two one}) Puppet::Parser::Files.find_manifests(pattern)[1].should == %w{one two} end end describe "when searching for manifests in a found module" do it "should return the name of the module and the manifests from the first found module" do mod = Puppet::Module.new("mymod") Puppet::Node::Environment.new.expects(:module).with("mymod").returns mod mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) Puppet::Parser::Files.find_manifests("mymod/init.pp").should == ["mymod", ["/one/mymod/manifests/init.pp"]] end it "should use the node environment if specified" do mod = Puppet::Module.new("mymod") Puppet::Node::Environment.new("myenv").expects(:module).with("mymod").returns mod mod.expects(:match_manifests).with("init.pp").returns(%w{/one/mymod/manifests/init.pp}) Puppet::Parser::Files.find_manifests("mymod/init.pp", :environment => "myenv")[1].should == ["/one/mymod/manifests/init.pp"] end after { Puppet.settings.clear } end end diff --git a/spec/unit/parser/functions/extlookup_spec.rb b/spec/unit/parser/functions/extlookup_spec.rb index 30962e137..59ecf39c0 100755 --- a/spec/unit/parser/functions/extlookup_spec.rb +++ b/spec/unit/parser/functions/extlookup_spec.rb @@ -1,95 +1,98 @@ #!/usr/bin/env rspec require 'spec_helper' require 'tempfile' describe "the extlookup function" do + include PuppetSpec::Files + before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new @scope.stubs(:environment).returns(Puppet::Node::Environment.new('production')) end it "should exist" do Puppet::Parser::Functions.function("extlookup").should == "function_extlookup" end it "should raise a ParseError if there is less than 1 arguments" do lambda { @scope.function_extlookup([]) }.should( raise_error(Puppet::ParseError)) end it "should raise a ParseError if there is more than 3 arguments" do lambda { @scope.function_extlookup(["foo", "bar", "baz", "gazonk"]) }.should( raise_error(Puppet::ParseError)) end it "should return the default" do result = @scope.function_extlookup([ "key", "default"]) result.should == "default" end it "should lookup the key in a supplied datafile" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value' t.puts 'nonkey,nonvalue' t.close result = @scope.function_extlookup([ "key", "default", t.path]) result.should == "value" end end it "should return an array if the datafile contains more than two columns" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value1,value2' t.puts 'nonkey,nonvalue,nonvalue' t.close result = @scope.function_extlookup([ "key", "default", t.path]) result.should == ["value1", "value2"] end end it "should raise an error if there's no matching key and no default" do t = Tempfile.new('extlookup.csv') do t.puts 'key,value' t.puts 'nonkey,nonvalue' t.close result = @scope.function_extlookup([ "key", nil, t.path]) result.should == "value" end end describe "should look in $extlookup_datadir for data files listed by $extlookup_precedence" do before do - @scope.stubs(:[]).with('::extlookup_datadir').returns("/tmp") - File.open("/tmp/one.csv","w"){|one| one.puts "key,value1" } - File.open("/tmp/two.csv","w") do |two| + dir = tmpdir('extlookup_datadir') + @scope.stubs(:[]).with('::extlookup_datadir').returns(dir) + File.open(File.join(dir, "one.csv"),"w"){|one| one.puts "key,value1" } + File.open(File.join(dir, "two.csv"),"w") do |two| two.puts "key,value2" two.puts "key2,value_two" end end it "when the key is in the first file" do @scope.stubs(:[]).with('::extlookup_precedence').returns(["one","two"]) result = @scope.function_extlookup([ "key" ]) result.should == "value1" end it "when the key is in the second file" do @scope.stubs(:[]).with('::extlookup_precedence').returns(["one","two"]) result = @scope.function_extlookup([ "key2" ]) result.should == "value_two" end it "should not modify extlookup_precedence data" do variable = '%{fqdn}' @scope.stubs(:[]).with('::extlookup_precedence').returns([variable,"one"]) @scope.stubs(:[]).with('::fqdn').returns('myfqdn') result = @scope.function_extlookup([ "key" ]) variable.should == '%{fqdn}' end end end diff --git a/spec/unit/parser/functions/sprintf_spec.rb b/spec/unit/parser/functions/sprintf_spec.rb index bd4863f23..3351c7fb3 100755 --- a/spec/unit/parser/functions/sprintf_spec.rb +++ b/spec/unit/parser/functions/sprintf_spec.rb @@ -1,43 +1,44 @@ #!/usr/bin/env rspec require 'spec_helper' describe "the sprintf function" do before :all do Puppet::Parser::Functions.autoloader.loadall end before :each do @scope = Puppet::Parser::Scope.new end it "should exist" do Puppet::Parser::Functions.function("sprintf").should == "function_sprintf" end it "should raise a ParseError if there is less than 1 argument" do lambda { @scope.function_sprintf([]) }.should( raise_error(Puppet::ParseError)) end it "should format integers" do result = @scope.function_sprintf(["%+05d", "23"]) result.should(eql("+0023")) end it "should format floats" do result = @scope.function_sprintf(["%+.2f", "2.7182818284590451"]) result.should(eql("+2.72")) end it "should format large floats" do result = @scope.function_sprintf(["%+.2e", "27182818284590451"]) - result.should(eql("+2.72e+16")) + str = Puppet.features.microsoft_windows? ? "+2.72e+016" : "+2.72e+16" + result.should(eql(str)) end it "should perform more complex formatting" do result = @scope.function_sprintf( [ "<%.8s:%#5o %#8X (%-8s)>", "overlongstring", "23", "48879", "foo" ]) result.should(eql("")) end end diff --git a/spec/unit/parser/type_loader_spec.rb b/spec/unit/parser/type_loader_spec.rb index 9367b61c8..fd991ffc4 100755 --- a/spec/unit/parser/type_loader_spec.rb +++ b/spec/unit/parser/type_loader_spec.rb @@ -1,230 +1,230 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/parser/type_loader' require 'puppet_spec/files' describe Puppet::Parser::TypeLoader do include PuppetSpec::Files before do @loader = Puppet::Parser::TypeLoader.new(:myenv) end it "should support an environment" do loader = Puppet::Parser::TypeLoader.new(:myenv) loader.environment.name.should == :myenv end it "should include the Environment Helper" do @loader.class.ancestors.should be_include(Puppet::Node::Environment::Helper) end it "should delegate its known resource types to its environment" do @loader.known_resource_types.should be_instance_of(Puppet::Resource::TypeCollection) end describe "when loading names from namespaces" do it "should do nothing if the name to import is an empty string" do @loader.expects(:name2files).never @loader.try_load_fqname(:hostclass, "") { |filename, modname| raise :should_not_occur }.should be_nil end it "should attempt to import each generated name" do @loader.expects(:import).with("foo/bar",nil).returns([]) @loader.expects(:import).with("foo",nil).returns([]) @loader.try_load_fqname(:hostclass, "foo::bar") { |f| false } end end describe "when importing" do before do Puppet::Parser::Files.stubs(:find_manifests).returns ["modname", %w{file}] Puppet::Parser::Parser.any_instance.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) Puppet::Parser::Parser.any_instance.stubs(:file=) end it "should return immediately when imports are being ignored" do Puppet::Parser::Files.expects(:find_manifests).never Puppet[:ignoreimport] = true @loader.import("foo").should be_nil end it "should find all manifests matching the file or pattern" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| pat == "myfile" }.returns ["modname", %w{one}] @loader.import("myfile") end it "should use the directory of the current file if one is set" do - Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == "/current" }.returns ["modname", %w{one}] - @loader.import("myfile", "/current/file") + Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:cwd] == make_absolute("/current") }.returns ["modname", %w{one}] + @loader.import("myfile", make_absolute("/current/file")) end it "should pass the environment when looking for files" do Puppet::Parser::Files.expects(:find_manifests).with { |pat, opts| opts[:environment] == @loader.environment }.returns ["modname", %w{one}] @loader.import("myfile") end it "should fail if no files are found" do Puppet::Parser::Files.expects(:find_manifests).returns [nil, []] lambda { @loader.import("myfile") }.should raise_error(Puppet::ImportError) end it "should parse each found file" do - Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] - @loader.expects(:parse_file).with("/one").returns(Puppet::Parser::AST::Hostclass.new('')) + Puppet::Parser::Files.expects(:find_manifests).returns ["modname", make_absolute("/one")] + @loader.expects(:parse_file).with(make_absolute("/one")).returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") end it "should make each file qualified before attempting to parse it" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{one}] - @loader.expects(:parse_file).with("/current/one").returns(Puppet::Parser::AST::Hostclass.new('')) - @loader.import("myfile", "/current/file") + @loader.expects(:parse_file).with(make_absolute("/current/one")).returns(Puppet::Parser::AST::Hostclass.new('')) + @loader.import("myfile", make_absolute("/current/file")) end it "should not attempt to import files that have already been imported" do Puppet::Parser::Files.expects(:find_manifests).returns ["modname", %w{/one}] Puppet::Parser::Parser.any_instance.expects(:parse).once.returns(Puppet::Parser::AST::Hostclass.new('')) @loader.import("myfile") # This will fail if it tries to reimport the file. @loader.import("myfile") end end describe "when importing all" do before do @base = tmpdir("base") # Create two module path directories @modulebase1 = File.join(@base, "first") FileUtils.mkdir_p(@modulebase1) @modulebase2 = File.join(@base, "second") FileUtils.mkdir_p(@modulebase2) - Puppet[:modulepath] = "#{@modulebase1}:#{@modulebase2}" + Puppet[:modulepath] = "#{@modulebase1}#{File::PATH_SEPARATOR}#{@modulebase2}" end def mk_module(basedir, name) module_dir = File.join(basedir, name) # Go ahead and make our manifest directory FileUtils.mkdir_p(File.join(module_dir, "manifests")) return Puppet::Module.new(name) end # We have to pass the base path so that we can # write to modules that are in the second search path def mk_manifests(base, mod, type, files) exts = {"ruby" => ".rb", "puppet" => ".pp"} files.collect do |file| name = mod.name + "::" + file.gsub("/", "::") path = File.join(base, mod.name, "manifests", file + exts[type]) FileUtils.mkdir_p(File.split(path)[0]) # write out the class if type == "ruby" File.open(path, "w") { |f| f.print "hostclass '#{name}' do\nend" } else File.open(path, "w") { |f| f.print "class #{name} {}" } end name end end it "should load all puppet manifests from all modules in the specified environment" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "puppet", %w{a b}) mk_manifests(@modulebase2, @module2, "puppet", %w{c d}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should load all ruby manifests from all modules in the specified environment" do @module1 = mk_module(@modulebase1, "one") @module2 = mk_module(@modulebase2, "two") mk_manifests(@modulebase1, @module1, "ruby", %w{a b}) mk_manifests(@modulebase2, @module2, "ruby", %w{c d}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::c").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("two::d").should be_instance_of(Puppet::Resource::Type) end it "should not load manifests from duplicate modules later in the module path" do @module1 = mk_module(@modulebase1, "one") # duplicate @module2 = mk_module(@modulebase2, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a}) mk_manifests(@modulebase2, @module2, "puppet", %w{c}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::c").should be_nil end it "should load manifests from subdirectories" do @module1 = mk_module(@modulebase1, "one") mk_manifests(@modulebase1, @module1, "puppet", %w{a a/b a/b/c}) @loader.import_all @loader.environment.known_resource_types.hostclass("one::a::b").should be_instance_of(Puppet::Resource::Type) @loader.environment.known_resource_types.hostclass("one::a::b::c").should be_instance_of(Puppet::Resource::Type) end end describe "when parsing a file" do before do @parser = Puppet::Parser::Parser.new(@loader.environment) @parser.stubs(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @parser.stubs(:file=) Puppet::Parser::Parser.stubs(:new).with(@loader.environment).returns @parser end it "should create a new parser instance for each file using the current environment" do Puppet::Parser::Parser.expects(:new).with(@loader.environment).returns @parser @loader.parse_file("/my/file") end it "should assign the parser its file and parse" do @parser.expects(:file=).with("/my/file") @parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')) @loader.parse_file("/my/file") end end it "should be able to add classes to the current resource type collection" do file = tmpfile("simple_file.pp") File.open(file, "w") { |f| f.puts "class foo {}" } @loader.import(file) @loader.known_resource_types.hostclass("foo").should be_instance_of(Puppet::Resource::Type) end describe "when deciding where to look for files" do { 'foo' => ['foo'], 'foo::bar' => ['foo/bar', 'foo'], 'foo::bar::baz' => ['foo/bar/baz', 'foo/bar', 'foo'] }.each do |fqname, expected_paths| it "should look for #{fqname.inspect} in #{expected_paths.inspect}" do @loader.instance_eval { name2files(fqname) }.should == expected_paths end end end end diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index 7728b5d40..99d4bc7c5 100755 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -1,409 +1,409 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/property' describe Puppet::Property do before do @class = Class.new(Puppet::Property) do @name = :foo end @class.initvars @provider = mock 'provider' @resource = stub 'resource', :provider => @provider @resource.stub_everything @property = @class.new :resource => @resource end it "should return its name as a string when converted to a string" do @property.to_s.should == @property.name.to_s end it "should be able to look up the modified name for a given value" do @class.newvalue(:foo) @class.value_name("foo").should == :foo end it "should be able to look up the modified name for a given value matching a regex" do @class.newvalue(%r{.}) @class.value_name("foo").should == %r{.} end it "should be able to look up a given value option" do @class.newvalue(:foo, :event => :whatever) @class.value_option(:foo, :event).should == :whatever end it "should be able to specify required features" do @class.should respond_to(:required_features=) end {"one" => [:one],:one => [:one],%w{a} => [:a],[:b] => [:b],%w{one two} => [:one,:two],[:a,:b] => [:a,:b]}.each { |in_value,out_value| it "should always convert required features into an array of symbols (e.g. #{in_value.inspect} --> #{out_value.inspect})" do @class.required_features = in_value @class.required_features.should == out_value end } it "should be able to shadow metaparameters" do @property.must respond_to(:shadow) end describe "when returning the default event name" do before do @resource = stub 'resource' @instance = @class.new(:resource => @resource) @instance.stubs(:should).returns "myval" end it "should use the current 'should' value to pick the event name" do @instance.expects(:should).returns "myvalue" @class.expects(:value_option).with('myvalue', :event).returns :event_name @instance.event_name end it "should return any event defined with the specified value" do @instance.expects(:should).returns :myval @class.expects(:value_option).with(:myval, :event).returns :event_name @instance.event_name.should == :event_name end describe "and the property is 'ensure'" do before do @instance.stubs(:name).returns :ensure @resource.expects(:type).returns :mytype end it "should use _created if the 'should' value is 'present'" do @instance.expects(:should).returns :present @instance.event_name.should == :mytype_created end it "should use _removed if the 'should' value is 'absent'" do @instance.expects(:should).returns :absent @instance.event_name.should == :mytype_removed end it "should use _changed if the 'should' value is not 'absent' or 'present'" do @instance.expects(:should).returns :foo @instance.event_name.should == :mytype_changed end it "should use _changed if the 'should value is nil" do @instance.expects(:should).returns nil @instance.event_name.should == :mytype_changed end end it "should use _changed if the property is not 'ensure'" do @instance.stubs(:name).returns :myparam @instance.expects(:should).returns :foo @instance.event_name.should == :myparam_changed end it "should use _changed if no 'should' value is set" do @instance.stubs(:name).returns :myparam @instance.expects(:should).returns nil @instance.event_name.should == :myparam_changed end end - describe "when creating an event" do + describe "when creating an event", :fails_on_windows => true do before do @event = Puppet::Transaction::Event.new # Use a real resource so we can test the event creation integration @resource = Puppet::Type.type(:mount).new :name => "foo" @instance = @class.new(:resource => @resource) @instance.stubs(:should).returns "myval" end it "should use an event from the resource as the base event" do event = Puppet::Transaction::Event.new @resource.expects(:event).returns event @instance.event.should equal(event) end it "should have the default event name" do @instance.expects(:event_name).returns :my_event @instance.event.name.should == :my_event end it "should have the property's name" do @instance.event.property.should == @instance.name.to_s end it "should have the 'should' value set" do @instance.stubs(:should).returns "foo" @instance.event.desired_value.should == "foo" end it "should provide its path as the source description" do @instance.stubs(:path).returns "/my/param" @instance.event.source_description.should == "/my/param" end end describe "when shadowing metaparameters" do before do @shadow_class = Class.new(Puppet::Property) do @name = :alias end @shadow_class.initvars end it "should create an instance of the metaparameter at initialization" do Puppet::Type.metaparamclass(:alias).expects(:new).with(:resource => @resource) @shadow_class.new :resource => @resource end it "should munge values using the shadow's munge method" do shadow = mock 'shadow' Puppet::Type.metaparamclass(:alias).expects(:new).returns shadow shadow.expects(:munge).with "foo" property = @shadow_class.new :resource => @resource property.munge("foo") end end describe "when defining new values" do it "should define a method for each value created with a block that's not a regex" do @class.newvalue(:foo) { } @property.must respond_to(:set_foo) end end describe "when assigning the value" do it "should just set the 'should' value" do @property.value = "foo" @property.should.must == "foo" end it "should validate each value separately" do @property.expects(:validate).with("one") @property.expects(:validate).with("two") @property.value = %w{one two} end it "should munge each value separately and use any result as the actual value" do @property.expects(:munge).with("one").returns :one @property.expects(:munge).with("two").returns :two # Do this so we get the whole array back. @class.array_matching = :all @property.value = %w{one two} @property.should.must == [:one, :two] end it "should return any set value" do (@property.value = :one).should == :one end end describe "when returning the value" do it "should return nil if no value is set" do @property.should.must be_nil end it "should return the first set 'should' value if :array_matching is set to :first" do @class.array_matching = :first @property.should = %w{one two} @property.should.must == "one" end it "should return all set 'should' values as an array if :array_matching is set to :all" do @class.array_matching = :all @property.should = %w{one two} @property.should.must == %w{one two} end it "should default to :first array_matching" do @class.array_matching.should == :first end it "should unmunge the returned value if :array_matching is set to :first" do @property.class.unmunge do |v| v.to_sym end @class.array_matching = :first @property.should = %w{one two} @property.should.must == :one end it "should unmunge all the returned values if :array_matching is set to :all" do @property.class.unmunge do |v| v.to_sym end @class.array_matching = :all @property.should = %w{one two} @property.should.must == [:one, :two] end end describe "when validating values" do it "should do nothing if no values or regexes have been defined" do lambda { @property.should = "foo" }.should_not raise_error end it "should fail if the value is not a defined value or alias and does not match a regex" do @class.newvalue(:foo) lambda { @property.should = "bar" }.should raise_error end it "should succeeed if the value is one of the defined values" do @class.newvalue(:foo) lambda { @property.should = :foo }.should_not raise_error end it "should succeeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do @class.newvalue(:foo) lambda { @property.should = "foo" }.should_not raise_error end it "should succeeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do @class.newvalue("foo") lambda { @property.should = :foo }.should_not raise_error end it "should succeed if the value is one of the defined aliases" do @class.newvalue("foo") @class.aliasvalue("bar", "foo") lambda { @property.should = :bar }.should_not raise_error end it "should succeed if the value matches one of the regexes" do @class.newvalue(/./) lambda { @property.should = "bar" }.should_not raise_error end it "should validate that all required features are present" do @class.newvalue(:foo, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns true @property.should = :foo end it "should fail if required features are missing" do @class.newvalue(:foo, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns false lambda { @property.should = :foo }.should raise_error(Puppet::Error) end it "should internally raise an ArgumentError if required features are missing" do @class.newvalue(:foo, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns false lambda { @property.validate_features_per_value :foo }.should raise_error(ArgumentError) end it "should validate that all required features are present for regexes" do value = @class.newvalue(/./, :required_features => [:a, :b]) @provider.expects(:satisfies?).with([:a, :b]).returns true @property.should = "foo" end it "should support specifying an individual required feature" do value = @class.newvalue(/./, :required_features => :a) @provider.expects(:satisfies?).returns true @property.should = "foo" end end describe "when munging values" do it "should do nothing if no values or regexes have been defined" do @property.munge("foo").should == "foo" end it "should return return any matching defined values" do @class.newvalue(:foo) @property.munge("foo").should == :foo end it "should return any matching aliases" do @class.newvalue(:foo) @class.aliasvalue(:bar, :foo) @property.munge("bar").should == :foo end it "should return the value if it matches a regex" do @class.newvalue(/./) @property.munge("bar").should == "bar" end it "should return the value if no other option is matched" do @class.newvalue(:foo) @property.munge("bar").should == "bar" end end describe "when syncing the 'should' value" do it "should set the value" do @class.newvalue(:foo) @property.should = :foo @property.expects(:set).with(:foo) @property.sync end end describe "when setting a value" do it "should catch exceptions and raise Puppet::Error" do @class.newvalue(:foo) { raise "eh" } lambda { @property.set(:foo) }.should raise_error(Puppet::Error) end describe "that was defined without a block" do it "should call the settor on the provider" do @class.newvalue(:bar) @provider.expects(:foo=).with :bar @property.set(:bar) end end describe "that was defined with a block" do it "should call the method created for the value if the value is not a regex" do @class.newvalue(:bar) {} @property.expects(:set_bar) @property.set(:bar) end it "should call the provided block if the value is a regex" do @class.newvalue(/./) { self.test } @property.expects(:test) @property.set("foo") end end end describe "when producing a change log" do it "should say 'defined' when the current value is 'absent'" do @property.change_to_s(:absent, "foo").should =~ /^defined/ end it "should say 'undefined' when the new value is 'absent'" do @property.change_to_s("foo", :absent).should =~ /^undefined/ end it "should say 'changed' when neither value is 'absent'" do @property.change_to_s("foo", "bar").should =~ /changed/ end end end diff --git a/spec/unit/provider/exec/shell_spec.rb b/spec/unit/provider/exec/shell_spec.rb index 90047b9d6..4e1f00281 100755 --- a/spec/unit/provider/exec/shell_spec.rb +++ b/spec/unit/provider/exec/shell_spec.rb @@ -1,50 +1,50 @@ #!/usr/bin/env rspec require 'spec_helper' provider_class = Puppet::Type.type(:exec).provider(:shell) -describe provider_class do +describe provider_class, :fails_on_windows => true do before :each do @resource = Puppet::Resource.new(:exec, 'foo') @provider = provider_class.new(@resource) end describe "#run" do it "should be able to run builtin shell commands" do output, status = @provider.run("if [ 1 = 1 ]; then echo 'blah'; fi") status.exitstatus.should == 0 output.should == "blah\n" end it "should be able to run commands with single quotes in them" do output, status = @provider.run("echo 'foo bar'") status.exitstatus.should == 0 output.should == "foo bar\n" end it "should be able to run commands with double quotes in them" do output, status = @provider.run('echo "foo bar"') status.exitstatus.should == 0 output.should == "foo bar\n" end it "should be able to run multiple commands separated by a semicolon" do output, status = @provider.run("echo 'foo' ; echo 'bar'") status.exitstatus.should == 0 output.should == "foo\nbar\n" end it "should be able to read values from the environment parameter" do @resource[:environment] = "FOO=bar" output, status = @provider.run("echo $FOO") status.exitstatus.should == 0 output.should == "bar\n" end end describe "#validatecmd" do it "should always return true because builtins don't need path or to be fully qualified" do @provider.validatecmd('whateverdoesntmatter').should == true end end end diff --git a/spec/unit/provider/host/parsed_spec.rb b/spec/unit/provider/host/parsed_spec.rb index 9cb5890cc..fa41d82e5 100755 --- a/spec/unit/provider/host/parsed_spec.rb +++ b/spec/unit/provider/host/parsed_spec.rb @@ -1,197 +1,197 @@ #!/usr/bin/env rspec require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' require 'puppet_spec/files' provider_class = Puppet::Type.type(:host).provider(:parsed) -describe provider_class do +describe provider_class, :fails_on_windows => true do include PuppetSpec::Files before do @host_class = Puppet::Type.type(:host) @provider = @host_class.provider(:parsed) @hostfile = tmpfile('hosts') @provider.any_instance.stubs(:target).returns @hostfile end after :each do @provider.initvars end def mkhost(args) hostresource = Puppet::Type::Host.new(:name => args[:name]) hostresource.stubs(:should).with(:target).returns @hostfile # Using setters of provider to build our testobject # Note: We already proved, that in case of host_aliases # the provider setter "host_aliases=(value)" will be # called with the joined array, so we just simulate that host = @provider.new(hostresource) args.each do |property,value| value = value.join(" ") if property == :host_aliases and value.is_a?(Array) host.send("#{property}=", value) end host end def genhost(host) @provider.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields host.flush @provider.target_object(@hostfile).read end describe "when parsing a line with ip and hostname" do it "should parse an ipv4 from the first field" do @provider.parse_line("127.0.0.1 localhost")[:ip].should == "127.0.0.1" end it "should parse an ipv6 from the first field" do @provider.parse_line("::1 localhost")[:ip].should == "::1" end it "should parse the name from the second field" do @provider.parse_line("::1 localhost")[:name].should == "localhost" end it "should set an empty comment" do @provider.parse_line("::1 localhost")[:comment].should == "" end it "should set host_aliases to :absent" do @provider.parse_line("::1 localhost")[:host_aliases].should == :absent end end describe "when parsing a line with ip, hostname and comment" do before do @testline = "127.0.0.1 localhost # A comment with a #-char" end it "should parse the ip from the first field" do @provider.parse_line(@testline)[:ip].should == "127.0.0.1" end it "should parse the hostname from the second field" do @provider.parse_line(@testline)[:name].should == "localhost" end it "should parse the comment after the first '#' character" do @provider.parse_line(@testline)[:comment].should == 'A comment with a #-char' end end describe "when parsing a line with ip, hostname and aliases" do it "should parse alias from the third field" do @provider.parse_line("127.0.0.1 localhost localhost.localdomain")[:host_aliases].should == "localhost.localdomain" end it "should parse multiple aliases" do @provider.parse_line("127.0.0.1 host alias1 alias2")[:host_aliases].should == 'alias1 alias2' @provider.parse_line("127.0.0.1 host alias1\talias2")[:host_aliases].should == 'alias1 alias2' @provider.parse_line("127.0.0.1 host alias1\talias2 alias3")[:host_aliases].should == 'alias1 alias2 alias3' end end describe "when parsing a line with ip, hostname, aliases and comment" do before do # Just playing with a few different delimiters @testline = "127.0.0.1\t host alias1\talias2 alias3 # A comment with a #-char" end it "should parse the ip from the first field" do @provider.parse_line(@testline)[:ip].should == "127.0.0.1" end it "should parse the hostname from the second field" do @provider.parse_line(@testline)[:name].should == "host" end it "should parse all host_aliases from the third field" do @provider.parse_line(@testline)[:host_aliases].should == 'alias1 alias2 alias3' end it "should parse the comment after the first '#' character" do @provider.parse_line(@testline)[:comment].should == 'A comment with a #-char' end end describe "when operating on /etc/hosts like files" do it_should_behave_like "all parsedfile providers", provider_class, my_fixtures('valid*') it "should be able to generate a simple hostfile entry" do host = mkhost( :name => 'localhost', :ip => '127.0.0.1', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost\n" end it "should be able to generate an entry with one alias" do host = mkhost( :name => 'localhost.localdomain', :ip => '127.0.0.1', :host_aliases => 'localhost', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost.localdomain\tlocalhost\n" end it "should be able to generate an entry with more than one alias" do host = mkhost( :name => 'host', :ip => '192.0.0.1', :host_aliases => [ 'a1','a2','a3','a4' ], :ensure => :present ) genhost(host).should == "192.0.0.1\thost\ta1 a2 a3 a4\n" end it "should be able to generate a simple hostfile entry with comments" do host = mkhost( :name => 'localhost', :ip => '127.0.0.1', :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost\t# Bazinga!\n" end it "should be able to generate an entry with one alias and a comment" do host = mkhost( :name => 'localhost.localdomain', :ip => '127.0.0.1', :host_aliases => 'localhost', :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "127.0.0.1\tlocalhost.localdomain\tlocalhost\t# Bazinga!\n" end it "should be able to generate an entry with more than one alias and a comment" do host = mkhost( :name => 'host', :ip => '192.0.0.1', :host_aliases => [ 'a1','a2','a3','a4' ], :comment => 'Bazinga!', :ensure => :present ) genhost(host).should == "192.0.0.1\thost\ta1 a2 a3 a4\t# Bazinga!\n" end end end diff --git a/spec/unit/provider/macauthorization_spec.rb b/spec/unit/provider/macauthorization_spec.rb index a76f917f7..dbe36a04b 100755 --- a/spec/unit/provider/macauthorization_spec.rb +++ b/spec/unit/provider/macauthorization_spec.rb @@ -1,147 +1,152 @@ #!/usr/bin/env rspec # # Unit testing for the macauthorization provider # require 'spec_helper' require 'puppet' require 'facter/util/plist' provider_class = Puppet::Type.type(:macauthorization).provider(:macauthorization) describe provider_class do before :each do # Create a mock resource @resource = stub 'resource' @authname = "foo.spam.eggs.puppettest" @authplist = {} @rules = {@authname => @authplist} authdb = {} authdb["rules"] = { "foorule" => "foo" } authdb["rights"] = { "fooright" => "foo" } # Stub out Plist::parse_xml Plist.stubs(:parse_xml).returns(authdb) # A catch all; no parameters set @resource.stubs(:[]).returns(nil) # But set name, ensure @resource.stubs(:[]).with(:name).returns @authname @resource.stubs(:[]).with(:ensure).returns :present @resource.stubs(:ref).returns "MacAuthorization[#{@authname}]" @provider = provider_class.new(@resource) end it "should have a create method" do @provider.should respond_to(:create) end it "should have a destroy method" do @provider.should respond_to(:destroy) end it "should have an exists? method" do @provider.should respond_to(:exists?) end it "should have a flush method" do @provider.should respond_to(:flush) end properties = [ :allow_root, :authenticate_user, :auth_class, :comment, :group, :k_of_n, :mechanisms, :rule, :session_owner, :shared, :timeout, :tries, :auth_type ] properties.each do |prop| it "should have a #{prop.to_s} method" do @provider.should respond_to(prop.to_s) end it "should have a #{prop.to_s}= method" do @provider.should respond_to(prop.to_s + "=") end end describe "when destroying a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method destroy_right" do @provider.expects(:destroy_right) @provider.destroy end it "should call the external command 'security authorizationdb remove @authname" do @provider.expects(:security).with("authorizationdb", :remove, @authname) @provider.destroy end end describe "when destroying a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method destroy_rule" do @provider.expects(:destroy_rule) @provider.destroy end end describe "when flushing a right" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:right) end it "should call the internal method flush_right" do @provider.expects(:flush_right) @provider.flush end it "should call the internal method set_right" do + @provider.expects(:execute).with { |cmds, args| + cmds.include?("read") and + cmds.include?(@authname) and + args[:combine] == false + }.once @provider.expects(:set_right) @provider.flush end it "should read and write to the auth database with the right arguments" do @provider.expects(:execute).with { |cmds, args| cmds.include?("read") and cmds.include?(@authname) and args[:combine] == false }.once @provider.expects(:execute).with { |cmds, args| cmds.include?("write") and cmds.include?(@authname) and args[:combine] == false and args[:stdinfile] != nil }.once @provider.flush end end describe "when flushing a rule" do before :each do @resource.stubs(:[]).with(:auth_type).returns(:rule) end it "should call the internal method flush_rule" do @provider.expects(:flush_rule) @provider.flush end it "should call the internal method set_rule" do @provider.expects(:set_rule) @provider.flush end end end diff --git a/spec/unit/provider/mount/parsed_spec.rb b/spec/unit/provider/mount/parsed_spec.rb index 7831dae3a..fdee2efab 100755 --- a/spec/unit/provider/mount/parsed_spec.rb +++ b/spec/unit/provider/mount/parsed_spec.rb @@ -1,293 +1,293 @@ #!/usr/bin/env rspec # # Created by Luke Kanies on 2007-9-12. # Copyright (c) 2006. All rights reserved. require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' provider_class = Puppet::Type.type(:mount).provider(:parsed) -describe provider_class do +describe provider_class, :fails_on_windows => true do before :each do @mount_class = Puppet::Type.type(:mount) @provider = @mount_class.provider(:parsed) end # LAK:FIXME I can't mock Facter because this test happens at parse-time. it "should default to /etc/vfstab on Solaris" do pending "This test only works on Solaris" unless Facter.value(:operatingsystem) == 'Solaris' Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/vfstab' end it "should default to /etc/fstab on anything else" do pending "This test does not work on Solaris" if Facter.value(:operatingsystem) == 'Solaris' Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/fstab' end describe "when parsing a line" do it "should not crash on incomplete lines in fstab" do parse = @provider.parse <<-FSTAB /dev/incomplete /dev/device name FSTAB lambda{ @provider.to_line(parse[0]) }.should_not raise_error end # it_should_behave_like "all parsedfile providers", # provider_class, my_fixtures('*.fstab') describe "on Solaris", :if => Facter.value(:operatingsystem) == 'Solaris', :'fails_on_ruby_1.9.2' => true do before :each do @example_line = "/dev/dsk/c0d0s0 /dev/rdsk/c0d0s0 \t\t / \t ufs 1 no\t-" end it "should extract device from the first field" do @provider.parse_line(@example_line)[:device].should == '/dev/dsk/c0d0s0' end it "should extract blockdevice from second field" do @provider.parse_line(@example_line)[:blockdevice].should == "/dev/rdsk/c0d0s0" end it "should extract name from third field" do @provider.parse_line(@example_line)[:name].should == "/" end it "should extract fstype from fourth field" do @provider.parse_line(@example_line)[:fstype].should == "ufs" end it "should extract pass from fifth field" do @provider.parse_line(@example_line)[:pass].should == "1" end it "should extract atboot from sixth field" do @provider.parse_line(@example_line)[:atboot].should == "no" end it "should extract options from seventh field" do @provider.parse_line(@example_line)[:options].should == "-" end end describe "on other platforms than Solaris", :if => Facter.value(:operatingsystem) != 'Solaris' do before :each do @example_line = "/dev/vg00/lv01\t/spare \t \t ext3 defaults\t1 2" end it "should extract device from the first field" do @provider.parse_line(@example_line)[:device].should == '/dev/vg00/lv01' end it "should extract name from second field" do @provider.parse_line(@example_line)[:name].should == "/spare" end it "should extract fstype from third field" do @provider.parse_line(@example_line)[:fstype].should == "ext3" end it "should extract options from fourth field" do @provider.parse_line(@example_line)[:options].should == "defaults" end it "should extract dump from fifth field" do @provider.parse_line(@example_line)[:dump].should == "1" end it "should extract options from sixth field" do @provider.parse_line(@example_line)[:pass].should == "2" end end end describe "mountinstances" do it "should get name from mountoutput found on Solaris" do Facter.stubs(:value).with(:operatingsystem).returns 'Solaris' @provider.stubs(:mountcmd).returns(File.read(my_fixture('solaris.mount'))) mounts = @provider.mountinstances mounts.size.should == 6 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/proc', :mounted => :yes } mounts[2].should == { :name => '/etc/mnttab', :mounted => :yes } mounts[3].should == { :name => '/tmp', :mounted => :yes } mounts[4].should == { :name => '/export/home', :mounted => :yes } mounts[5].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on HP-UX" do Facter.stubs(:value).with(:operatingsystem).returns 'HP-UX' @provider.stubs(:mountcmd).returns(File.read(my_fixture('hpux.mount'))) mounts = @provider.mountinstances mounts.size.should == 17 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/devices', :mounted => :yes } mounts[2].should == { :name => '/dev', :mounted => :yes } mounts[3].should == { :name => '/system/contract', :mounted => :yes } mounts[4].should == { :name => '/proc', :mounted => :yes } mounts[5].should == { :name => '/etc/mnttab', :mounted => :yes } mounts[6].should == { :name => '/etc/svc/volatile', :mounted => :yes } mounts[7].should == { :name => '/system/object', :mounted => :yes } mounts[8].should == { :name => '/etc/dfs/sharetab', :mounted => :yes } mounts[9].should == { :name => '/lib/libc.so.1', :mounted => :yes } mounts[10].should == { :name => '/dev/fd', :mounted => :yes } mounts[11].should == { :name => '/tmp', :mounted => :yes } mounts[12].should == { :name => '/var/run', :mounted => :yes } mounts[13].should == { :name => '/export', :mounted => :yes } mounts[14].should == { :name => '/export/home', :mounted => :yes } mounts[15].should == { :name => '/rpool', :mounted => :yes } mounts[16].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on Darwin" do Facter.stubs(:value).with(:operatingsystem).returns 'Darwin' @provider.stubs(:mountcmd).returns(File.read(my_fixture('darwin.mount'))) mounts = @provider.mountinstances mounts.size.should == 6 mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/dev', :mounted => :yes } mounts[2].should == { :name => '/net', :mounted => :yes } mounts[3].should == { :name => '/home', :mounted => :yes } mounts[4].should == { :name => '/usr', :mounted => :yes } mounts[5].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on Linux" do Facter.stubs(:value).with(:operatingsystem).returns 'Gentoo' @provider.stubs(:mountcmd).returns(File.read(my_fixture('linux.mount'))) mounts = @provider.mountinstances mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/lib64/rc/init.d', :mounted => :yes } mounts[2].should == { :name => '/sys', :mounted => :yes } mounts[3].should == { :name => '/usr/portage', :mounted => :yes } mounts[4].should == { :name => '/ghost', :mounted => :yes } end it "should get name from mountoutput found on AIX" do Facter.stubs(:value).with(:operatingsystem).returns 'AIX' @provider.stubs(:mountcmd).returns(File.read(my_fixture('aix.mount'))) mounts = @provider.mountinstances mounts[0].should == { :name => '/', :mounted => :yes } mounts[1].should == { :name => '/tmp', :mounted => :yes } mounts[2].should == { :name => '/home', :mounted => :yes } mounts[3].should == { :name => '/usr', :mounted => :yes } mounts[4].should == { :name => '/usr/code', :mounted => :yes } end it "should raise an error if a line is not understandable" do @provider.stubs(:mountcmd).returns("bazinga!") lambda { @provider.mountinstances }.should raise_error Puppet::Error end end it "should support AIX's paragraph based /etc/filesystems" my_fixtures('*.fstab').each do |fstab| platform = File.basename(fstab, '.fstab') describe "when calling instances on #{platform}" do before :each do if Facter[:operatingsystem] == "Solaris" then platform == 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" else platform != 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" end # Stub the mount output to our fixture. begin mount = my_fixture(platform + '.mount') @provider.stubs(:mountcmd).returns File.read(mount) rescue pending "is #{platform}.mount missing at this point?" end # Note: we have to stub default_target before creating resources # because it is used by Puppet::Type::Mount.new to populate the # :target property. @provider.stubs(:default_target).returns fstab @retrieve = @provider.instances.collect { |prov| {:name => prov.get(:name), :ensure => prov.get(:ensure)}} end # Following mountpoint are present in all fstabs/mountoutputs it "should include unmounted resources" do @retrieve.should include(:name => '/', :ensure => :mounted) end it "should include mounted resources" do @retrieve.should include(:name => '/boot', :ensure => :unmounted) end it "should include ghost resources" do @retrieve.should include(:name => '/ghost', :ensure => :ghost) end end describe "when prefetching on #{platform}" do before :each do if Facter[:operatingsystem] == "Solaris" then platform == 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" else platform != 'solaris' or pending "We need to stub the operatingsystem fact at load time, but can't" end # Stub the mount output to our fixture. begin mount = my_fixture(platform + '.mount') @provider.stubs(:mountcmd).returns File.read(mount) rescue pending "is #{platform}.mount missing at this point?" end # Note: we have to stub default_target before creating resources # because it is used by Puppet::Type::Mount.new to populate the # :target property. @provider.stubs(:default_target).returns fstab @res_ghost = Puppet::Type::Mount.new(:name => '/ghost') # in no fake fstab @res_mounted = Puppet::Type::Mount.new(:name => '/') # in every fake fstab @res_unmounted = Puppet::Type::Mount.new(:name => '/boot') # in every fake fstab @res_absent = Puppet::Type::Mount.new(:name => '/absent') # in no fake fstab # Simulate transaction.rb:prefetch @resource_hash = {} [@res_ghost, @res_mounted, @res_unmounted, @res_absent].each do |resource| @resource_hash[resource.name] = resource end end it "should set :ensure to :unmounted if found in fstab but not mounted" do @provider.prefetch(@resource_hash) @res_unmounted.provider.get(:ensure).should == :unmounted end it "should set :ensure to :ghost if not found in fstab but mounted" do @provider.prefetch(@resource_hash) @res_ghost.provider.get(:ensure).should == :ghost end it "should set :ensure to :mounted if found in fstab and mounted" do @provider.prefetch(@resource_hash) @res_mounted.provider.get(:ensure).should == :mounted end it "should set :ensure to :absent if not found in fstab and not mounted" do @provider.prefetch(@resource_hash) @res_absent.provider.get(:ensure).should == :absent end end end end diff --git a/spec/unit/provider/service/smf_spec.rb b/spec/unit/provider/service/smf_spec.rb index 5212d540a..fd7d50e3a 100755 --- a/spec/unit/provider/service/smf_spec.rb +++ b/spec/unit/provider/service/smf_spec.rb @@ -1,137 +1,138 @@ #!/usr/bin/env rspec # # Unit testing for the SMF service Provider # # author Dominic Cleal # require 'spec_helper' provider_class = Puppet::Type.type(:service).provider(:smf) describe provider_class do before(:each) do # Create a mock resource @resource = Puppet::Type.type(:service).new( :name => "/system/myservice", :ensure => :running, :enable => :true) @provider = provider_class.new(@resource) FileTest.stubs(:file?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:executable?).with('/usr/sbin/svcadm').returns true FileTest.stubs(:file?).with('/usr/bin/svcs').returns true FileTest.stubs(:executable?).with('/usr/bin/svcs').returns true end it "should have a restart method" do @provider.should respond_to(:restart) end it "should have a restartcmd method" do @provider.should respond_to(:restartcmd) end it "should have a start method" do @provider.should respond_to(:start) end it "should have a stop method" do @provider.should respond_to(:stop) end it "should have an enabled? method" do @provider.should respond_to(:enabled?) end it "should have an enable method" do @provider.should respond_to(:enable) end it "should have a disable method" do @provider.should respond_to(:disable) end describe "when checking status" do it "should call the external command 'svcs /system/myservice' once" do @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.status end it "should return stopped if svcs can't find the service" do @provider.stubs(:svcs).raises(Puppet::ExecutionFailure.new("no svc found")) @provider.status.should == :stopped end it "should return running if online in svcs output" do @provider.stubs(:svcs).returns("online\t-") @provider.status.should == :running end it "should return stopped if disabled in svcs output" do @provider.stubs(:svcs).returns("disabled\t-") @provider.status.should == :stopped end it "should return maintenance if in maintenance in svcs output" do @provider.stubs(:svcs).returns("maintenance\t-") @provider.status.should == :maintenance end it "should return target state if transitioning in svcs output" do @provider.stubs(:svcs).returns("online\tdisabled") @provider.status.should == :stopped end it "should throw error if it's a legacy service in svcs output" do @provider.stubs(:svcs).returns("legacy_run\t-") lambda { @provider.status }.should raise_error(Puppet::Error, "Cannot manage legacy services through SMF") end end describe "when starting" do it "should enable the service if it is not enabled" do @provider.expects(:status).returns :stopped @provider.expects(:texecute) @provider.start end it "should always execute external command 'svcadm enable /system/myservice'" do @provider.stubs(:status).returns :running @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "/system/myservice"], true) @provider.start end it "should execute external command 'svcadm clear /system/myservice' if in maintenance" do @provider.stubs(:status).returns :maintenance @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :clear, "/system/myservice"], true) @provider.start end end describe "when starting a service with a manifest" do before(:each) do @resource = Puppet::Type.type(:service).new(:name => "/system/myservice", :ensure => :running, :enable => :true, :manifest => "/tmp/myservice.xml") @provider = provider_class.new(@resource) $CHILD_STATUS.stubs(:exitstatus).returns(1) end it "should import the manifest if service is missing" do @provider.expects(:svccfg).with(:import, "/tmp/myservice.xml") @provider.expects(:texecute).with(:start, ["/usr/sbin/svcadm", :enable, "/system/myservice"], true) + @provider.expects(:svcs).with('-H', '-o', 'state,nstate', "/system/myservice").returns("online\t-") @provider.start end it "should handle failures if importing a manifest" do @provider.expects(:svccfg).raises(Puppet::ExecutionFailure.new("can't svccfg import")) lambda { @provider.start }.should raise_error(Puppet::Error, "Cannot config /system/myservice to enable it: can't svccfg import") end end describe "when stopping" do it "should execute external command 'svcadm disable /system/myservice'" do @provider.expects(:texecute).with(:stop, ["/usr/sbin/svcadm", :disable, "/system/myservice"], true) @provider.stop end end describe "when restarting" do it "should call 'svcadm restart /system/myservice'" do @provider.expects(:texecute).with(:restart, ["/usr/sbin/svcadm", :restart, "/system/myservice"], true) @provider.restart end end end diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb index bd5e55a9e..8a7fe755c 100755 --- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb @@ -1,200 +1,200 @@ #!/usr/bin/env rspec require 'spec_helper' require 'shared_behaviours/all_parsedfile_providers' require 'puppet_spec/files' provider_class = Puppet::Type.type(:ssh_authorized_key).provider(:parsed) describe provider_class do include PuppetSpec::Files before :each do @keyfile = tmpfile('authorized_keys') @provider_class = provider_class @provider_class.initvars @provider_class.any_instance.stubs(:target).returns @keyfile @user = 'random_bob' Puppet::Util.stubs(:uid).with(@user).returns 12345 end def mkkey(args) args[:target] = @keyfile args[:user] = @user resource = Puppet::Type.type(:ssh_authorized_key).new(args) key = @provider_class.new(resource) args.each do |p,v| key.send(p.to_s + "=", v) end key end def genkey(key) @provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) File.stubs(:chown) File.stubs(:chmod) Puppet::Util::SUIDManager.stubs(:asuser).yields key.flush @provider_class.target_object(@keyfile).read end it_should_behave_like "all parsedfile providers", provider_class it "should be able to generate a basic authorized_keys file" do key = mkkey(:name => "Just_Testing", :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", :type => "ssh-dss", :ensure => :present, :options => [:absent] ) genkey(key).should == "ssh-dss AAAAfsfddsjldjgksdflgkjsfdlgkj Just_Testing\n" end it "should be able to generate a authorized_keys file with options" do key = mkkey(:name => "root@localhost", :key => "AAAAfsfddsjldjgksdflgkjsfdlgkj", :type => "ssh-rsa", :ensure => :present, :options => ['from="192.168.1.1"', "no-pty", "no-X11-forwarding"] ) genkey(key).should == "from=\"192.168.1.1\",no-pty,no-X11-forwarding ssh-rsa AAAAfsfddsjldjgksdflgkjsfdlgkj root@localhost\n" end it "should be able to parse options containing commas via its parse_options method" do options = %w{from="host1.reductlivelabs.com,host.reductivelabs.com" command="/usr/local/bin/run" ssh-pty} optionstr = options.join(", ") @provider_class.parse_options(optionstr).should == options end it "should use '' as name for entries that lack a comment" do line = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAut8aOSxenjOqF527dlsdHWV4MNoAsX14l9M297+SQXaQ5Z3BedIxZaoQthkDALlV/25A1COELrg9J2MqJNQc8Xe9XQOIkBQWWinUlD/BXwoOTWEy8C8zSZPHZ3getMMNhGTBO+q/O+qiJx3y5cA4MTbw2zSxukfWC87qWwcZ64UUlegIM056vPsdZWFclS9hsROVEa57YUMrehQ1EGxT4Z5j6zIopufGFiAPjZigq/vqgcAqhAKP6yu4/gwO6S9tatBeEjZ8fafvj1pmvvIplZeMr96gHE7xS3pEEQqnB3nd4RY7AF6j9kFixnsytAUO7STPh/M3pLiVQBN89TvWPQ==" @provider_class.parse(line)[0][:name].should == "" end end describe provider_class do before :each do @resource = Puppet::Type.type(:ssh_authorized_key).new(:name => "foo", :user => "random_bob") @provider = provider_class.new(@resource) provider_class.stubs(:filetype).returns(Puppet::Util::FileType::FileTypeRam) Puppet::Util::SUIDManager.stubs(:asuser).yields provider_class.initvars end describe "when flushing" do before :each do # Stub file and directory operations Dir.stubs(:mkdir) File.stubs(:chmod) File.stubs(:chown) end describe "and both a user and a target have been specified" do before :each do Puppet::Util.stubs(:uid).with("random_bob").returns 12345 @resource[:user] = "random_bob" target = "/tmp/.ssh_dir/place_to_put_authorized_keys" @resource[:target] = target end it "should create the directory" do File.stubs(:exist?).with("/tmp/.ssh_dir").returns false Dir.expects(:mkdir).with("/tmp/.ssh_dir", 0700) @provider.flush end it "should chown the directory to the user" do uid = Puppet::Util.uid("random_bob") File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir") @provider.flush end it "should chown the key file to the user" do uid = Puppet::Util.uid("random_bob") File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir/place_to_put_authorized_keys") @provider.flush end it "should chmod the key file to 0600" do File.expects(:chmod).with(0600, "/tmp/.ssh_dir/place_to_put_authorized_keys") @provider.flush end end - describe "and a user has been specified with no target" do + describe "and a user has been specified with no target", :fails_on_windows => true do before :each do @resource[:user] = "nobody" # # I'd like to use random_bob here and something like # # File.stubs(:expand_path).with("~random_bob/.ssh").returns "/users/r/random_bob/.ssh" # # but mocha objects strenuously to stubbing File.expand_path # so I'm left with using nobody. @dir = File.expand_path("~nobody/.ssh") end it "should create the directory if it doesn't exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).with(@dir,0700) @provider.flush end it "should not create or chown the directory if it already exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never @provider.flush end it "should chown the directory to the user if it creates it" do File.stubs(:exist?).with(@dir).returns false Dir.stubs(:mkdir).with(@dir,0700) uid = Puppet::Util.uid("nobody") File.expects(:chown).with(uid, nil, @dir) @provider.flush end it "should not create or chown the directory if it already exist" do File.stubs(:exist?).with(@dir).returns false Dir.expects(:mkdir).never File.expects(:chown).never @provider.flush end it "should chown the key file to the user" do uid = Puppet::Util.uid("nobody") File.expects(:chown).with(uid, nil, File.expand_path("~nobody/.ssh/authorized_keys")) @provider.flush end it "should chmod the key file to 0600" do File.expects(:chmod).with(0600, File.expand_path("~nobody/.ssh/authorized_keys")) @provider.flush end end describe "and a target has been specified with no user" do it "should raise an error" do @resource = Puppet::Type.type(:ssh_authorized_key).new(:name => "foo", :target => "/tmp/.ssh_dir/place_to_put_authorized_keys") @provider = provider_class.new(@resource) proc { @provider.flush }.should raise_error end end - describe "and a invalid user has been specified with no target" do + describe "and a invalid user has been specified with no target", :fails_on_windows => true do it "should catch an exception and raise a Puppet error" do @resource[:user] = "thisusershouldnotexist" lambda { @provider.flush }.should raise_error(Puppet::Error) end end end end diff --git a/spec/unit/provider/user/user_role_add_spec.rb b/spec/unit/provider/user/user_role_add_spec.rb index 5f2fc306e..c44fc5a65 100755 --- a/spec/unit/provider/user/user_role_add_spec.rb +++ b/spec/unit/provider/user/user_role_add_spec.rb @@ -1,266 +1,266 @@ #!/usr/bin/env rspec require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:user_role_add) -describe provider_class do +describe provider_class, :fails_on_windows => true do before do @resource = stub("resource", :name => "myuser", :managehome? => nil) @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @resource.stubs(:allowdupe?).returns false @provider = provider_class.new(@resource) end describe "when calling command" do before do klass = stub("provider") klass.stubs(:command).with(:foo).returns("userfoo") klass.stubs(:command).with(:role_foo).returns("rolefoo") @provider.stubs(:class).returns(klass) end it "should use the command if not a role and ensure!=role" do @provider.stubs(:is_role?).returns(false) @provider.stubs(:exists?).returns(false) @resource.stubs(:[]).with(:ensure).returns(:present) @provider.command(:foo).should == "userfoo" end it "should use the role command when a role" do @provider.stubs(:is_role?).returns(true) @provider.command(:foo).should == "rolefoo" end it "should use the role command when !exists and ensure=role" do @provider.stubs(:is_role?).returns(false) @provider.stubs(:exists?).returns(false) @resource.stubs(:[]).with(:ensure).returns(:role) @provider.command(:foo).should == "rolefoo" end end describe "when calling transition", :'fails_on_ruby_1.9.2' => true do it "should return the type set to whatever is passed in" do @provider.expects(:command).with(:modify).returns("foomod") @provider.transition("bar").include?("type=bar") end end describe "when calling create" do before do @provider.stubs(:password=) end it "should use the add command when the user is not a role" do @provider.stubs(:is_role?).returns(false) @provider.expects(:addcmd).returns("useradd") @provider.expects(:run).at_least_once @provider.create end it "should use transition(normal) when the user is a role" do @provider.stubs(:is_role?).returns(true) @provider.expects(:transition).with("normal") @provider.expects(:run) @provider.create end it "should set password age rules" do @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :user_role_add @provider = provider_class.new(@resource) @provider.stubs(:user_attributes) @provider.stubs(:execute) @provider.expects(:execute).with { |cmd, *args| args == ["-n", 5, "-x", 10, "myuser"] } @provider.create end end describe "when calling destroy" do it "should use the delete command if the user exists and is not a role" do @provider.stubs(:exists?).returns(true) @provider.stubs(:is_role?).returns(false) @provider.expects(:deletecmd) @provider.expects(:run) @provider.destroy end it "should use the delete command if the user is a role" do @provider.stubs(:exists?).returns(true) @provider.stubs(:is_role?).returns(true) @provider.expects(:deletecmd) @provider.expects(:run) @provider.destroy end end describe "when calling create_role" do it "should use the transition(role) if the user exists" do @provider.stubs(:exists?).returns(true) @provider.stubs(:is_role?).returns(false) @provider.expects(:transition).with("role") @provider.expects(:run) @provider.create_role end it "should use the add command when role doesn't exists" do @provider.stubs(:exists?).returns(false) @provider.expects(:addcmd) @provider.expects(:run) @provider.create_role end end describe "when allow duplicate is enabled" do before do @resource.expects(:allowdupe?).returns true @resource.stubs(:system?) @provider.stubs(:is_role?).returns(false) @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-o") } end it "should add -o when the user is being created", :'fails_on_ruby_1.9.2' => true do @provider.stubs(:password=) @provider.create end it "should add -o when the uid is being modified" do @provider.uid = 150 end end [:roles, :auths, :profiles].each do |val| describe "when getting #{val}" do it "should get the user_attributes" do @provider.expects(:user_attributes) @provider.send(val) end it "should get the #{val} attribute" do attributes = mock("attributes") attributes.expects(:[]).with(val) @provider.stubs(:user_attributes).returns(attributes) @provider.send(val) end end end describe "when getting the keys" do it "should get the user_attributes" do @provider.expects(:user_attributes) @provider.keys end it "should call removed_managed_attributes" do @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) @provider.expects(:remove_managed_attributes) @provider.keys end it "should removed managed attribute (type, auths, roles, etc)" do @provider.stubs(:user_attributes).returns({ :type => "normal", :foo => "something" }) @provider.keys.should == { :foo => "something" } end end describe "when adding properties" do it "should call build_keys_cmd" do @resource.stubs(:should).returns "" @resource.expects(:should).with(:keys).returns({ :foo => "bar" }) @provider.expects(:build_keys_cmd).returns([]) @provider.add_properties end it "should add the elements of the keys hash to an array" do @resource.stubs(:should).returns "" @resource.expects(:should).with(:keys).returns({ :foo => "bar"}) @provider.add_properties.must == ["-K", "foo=bar"] end end describe "when calling build_keys_cmd" do it "should build cmd array with keypairs seperated by -K ending with user" do @provider.build_keys_cmd({"foo" => "bar", "baz" => "boo"}).should.eql? ["-K", "foo=bar", "-K", "baz=boo"] end end describe "when setting the keys" do before do @provider.stubs(:is_role?).returns(false) end it "should run a command" do @provider.expects(:run) @provider.keys=({}) end it "should build the command" do @resource.stubs(:[]).with(:name).returns("someuser") @provider.stubs(:command).returns("usermod") @provider.expects(:build_keys_cmd).returns(["-K", "foo=bar"]) @provider.expects(:run).with(["usermod", "-K", "foo=bar", "someuser"], "modify attribute key pairs") @provider.keys=({}) end end describe "when getting the hashed password" do before do @array = mock "array" end it "should readlines of /etc/shadow" do File.expects(:readlines).with("/etc/shadow").returns([]) @provider.password end it "should reject anything that doesn't start with alpha numerics" do @array.expects(:reject).returns([]) File.stubs(:readlines).with("/etc/shadow").returns(@array) @provider.password end it "should collect splitting on ':'" do @array.stubs(:reject).returns(@array) @array.expects(:collect).returns([]) File.stubs(:readlines).with("/etc/shadow").returns(@array) @provider.password end it "should find the matching user" do @resource.stubs(:[]).with(:name).returns("username") @array.stubs(:reject).returns(@array) @array.stubs(:collect).returns([["username", "hashedpassword"], ["someoneelse", "theirpassword"]]) File.stubs(:readlines).with("/etc/shadow").returns(@array) @provider.password.must == "hashedpassword" end it "should get the right password" do @resource.stubs(:[]).with(:name).returns("username") File.stubs(:readlines).with("/etc/shadow").returns(["#comment", " nonsense", " ", "username:hashedpassword:stuff:foo:bar:::", "other:pword:yay:::"]) @provider.password.must == "hashedpassword" end end describe "when setting the password" do #how can you mock these blocks up? it "should open /etc/shadow for reading and /etc/shadow_tmp for writing" do File.expects(:open).with("/etc/shadow", "r") File.stubs(:rename) @provider.password=("hashedpassword") end it "should rename the /etc/shadow_tmp to /etc/shadow" do File.stubs(:open).with("/etc/shadow", "r") File.expects(:rename).with("/etc/shadow_tmp", "/etc/shadow") @provider.password=("hashedpassword") end end describe "#shadow_entry" do it "should return the line for the right user" do File.stubs(:readlines).returns(["someuser:!:10:5:20:7:1::\n", "fakeval:*:20:10:30:7:2::\n", "testuser:*:30:15:40:7:3::\n"]) @provider.shadow_entry.should == ["fakeval", "*", "20", "10", "30", "7", "2"] end end end diff --git a/spec/unit/provider/user/useradd_spec.rb b/spec/unit/provider/user/useradd_spec.rb index 724fc12c0..4265ee3a0 100755 --- a/spec/unit/provider/user/useradd_spec.rb +++ b/spec/unit/provider/user/useradd_spec.rb @@ -1,215 +1,215 @@ #!/usr/bin/env rspec require 'spec_helper' provider_class = Puppet::Type.type(:user).provider(:useradd) -describe provider_class do +describe provider_class, :fails_on_windows => true do before do @resource = stub("resource", :name => "myuser", :managehome? => nil) @resource.stubs(:should).returns "fakeval" @resource.stubs(:[]).returns "fakeval" @provider = provider_class.new(@resource) end # #1360 it "should add -o when allowdupe is enabled and the user is being created" do @resource.expects(:allowdupe?).returns true @resource.expects(:system?).returns true @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-o") } @provider.create end it "should add -o when allowdupe is enabled and the uid is being modified" do @resource.expects(:allowdupe?).returns true @provider.expects(:execute).with { |args| args.include?("-o") } @provider.uid = 150 end it "should add -r when system is enabled" do @resource.expects(:allowdupe?).returns true @resource.expects(:system?).returns true @provider.stubs(:execute) @provider.expects(:execute).with { |args| args.include?("-r") } @provider.create end it "should set password age rules" do provider_class.has_feature :manages_password_age @resource = Puppet::Type.type(:user).new :name => "myuser", :password_min_age => 5, :password_max_age => 10, :provider => :useradd @provider = provider_class.new(@resource) @provider.stubs(:execute) @provider.expects(:execute).with { |cmd, *args| args == ["-m", 5, "-M", 10, "myuser"] } @provider.create end describe "when checking to add allow dup" do it "should check allow dup" do @resource.expects(:allowdupe?) @provider.check_allow_dup end it "should return an array with a flag if dup is allowed" do @resource.stubs(:allowdupe?).returns true @provider.check_allow_dup.must == ["-o"] end it "should return an empty array if no dup is allowed" do @resource.stubs(:allowdupe?).returns false @provider.check_allow_dup.must == [] end end describe "when checking to add system users" do it "should check system users" do @resource.expects(:system?) @provider.check_system_users end it "should return an array with a flag if it's a system user" do @resource.stubs(:system?).returns true @provider.check_system_users.must == ["-r"] end it "should return an empty array if it's not a system user" do @resource.stubs(:system?).returns false @provider.check_system_users.must == [] end end describe "when checking manage home" do it "should check manage home" do @resource.expects(:managehome?) @provider.check_manage_home end it "should return an array with -m flag if home is managed" do @resource.stubs(:managehome?).returns true @provider.check_manage_home.must == ["-m"] end it "should return an array with -M if home is not managed and on Redhat" do Facter.stubs(:value).with("operatingsystem").returns("RedHat") @resource.stubs(:managehome?).returns false @provider.check_manage_home.must == ["-M"] end it "should return an empty array if home is not managed and not on Redhat" do Facter.stubs(:value).with("operatingsystem").returns("some OS") @resource.stubs(:managehome?).returns false @provider.check_manage_home.must == [] end end describe "when adding properties" do it "should get the valid properties" it "should not add the ensure property" it "should add the flag and value to an array" it "should return and array of flags and values" end describe "when calling addcmd" do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true @resource.stubs(:system?).returns true end it "should call command with :add" do @provider.expects(:command).with(:add) @provider.addcmd end it "should add properties" do @provider.expects(:add_properties).returns([]) @provider.addcmd end it "should check and add if dup allowed" do @provider.expects(:check_allow_dup).returns([]) @provider.addcmd end it "should check and add if home is managed" do @provider.expects(:check_manage_home).returns([]) @provider.addcmd end it "should add the resource :name" do @resource.expects(:[]).with(:name) @provider.addcmd end it "should return an array with -r if system? is true" do resource = Puppet::Type.type(:user).new( :name => "bob", :system => true) provider_class.new( resource ).addcmd.should include("-r") end it "should return an array without -r if system? is false" do resource = Puppet::Type.type(:user).new( :name => "bob", :system => false) provider_class.new( resource ).addcmd.should_not include("-r") end it "should return an array with full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns("somedate") @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", '-e somedate', "-r", "someuser"] end it "should return an array without -e if expiry is undefined full command" do @provider.stubs(:command).with(:add).returns("useradd") @provider.stubs(:add_properties).returns(["-G", "somegroup"]) @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:[]).with(:expiry).returns nil @provider.addcmd.must == ["useradd", "-G", "somegroup", "-o", "-m", "-r", "someuser"] end end describe "when calling passcmd" do before do @resource.stubs(:allowdupe?).returns true @resource.stubs(:managehome?).returns true @resource.stubs(:system?).returns true end it "should call command with :pass" do @provider.expects(:command).with(:password) @provider.passcmd end it "should return nil if neither min nor max is set" do @resource.stubs(:should).with(:password_min_age).returns nil @resource.stubs(:should).with(:password_max_age).returns nil @provider.passcmd.must == nil end it "should return a chage command array with -m and the user name if password_min_age is set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns 123 @resource.stubs(:should).with(:password_max_age).returns nil @provider.passcmd.must == ['chage','-m',123,'someuser'] end it "should return a chage command array with -M if password_max_age is set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns nil @resource.stubs(:should).with(:password_max_age).returns 999 @provider.passcmd.must == ['chage','-M',999,'someuser'] end it "should return a chage command array with -M -m if both password_min_age and password_max_age are set" do @provider.stubs(:command).with(:password).returns("chage") @resource.stubs(:[]).with(:name).returns("someuser") @resource.stubs(:should).with(:password_min_age).returns 123 @resource.stubs(:should).with(:password_max_age).returns 999 @provider.passcmd.must == ['chage','-m',123,'-M',999,'someuser'] end end end diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb index 8f4910af6..5b914dac3 100755 --- a/spec/unit/resource/catalog_spec.rb +++ b/spec/unit/resource/catalog_spec.rb @@ -1,1062 +1,1063 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Resource::Catalog, "when compiling" do + include PuppetSpec::Files before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + @basepath = make_absolute("/somepath") # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) end it "should be an Expirer" do Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer) end it "should always be expired if it's not applying" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.expects(:applying?).returns false @catalog.should be_dependent_data_expired(Time.now) end it "should not be expired if it's applying and the timestamp is late enough" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.expire @catalog.expects(:applying?).returns true @catalog.should_not be_dependent_data_expired(Time.now) end it "should be able to write its list of classes to the class file" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.add_class "foo", "bar" Puppet.settings.expects(:value).with(:classfile).returns "/class/file" fh = mock 'filehandle' File.expects(:open).with("/class/file", "w").yields fh fh.expects(:puts).with "foo\nbar" @catalog.write_class_file end it "should have a client_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.client_version = 5 @catalog.client_version.should == 5 end it "should have a server_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.server_version = 5 @catalog.server_version.should == 5 end describe "when compiling" do it "should accept tags" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one") config.tags.should == %w{one} end it "should accept multiple tags at once" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", "two") config.tags.should == %w{one two} end it "should convert all tags to strings" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", :two) config.tags.should == %w{one two} end it "should tag with both the qualified name and the split name" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one::two") config.tags.include?("one").should be_true config.tags.include?("one::two").should be_true end it "should accept classes" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.classes.should == %w{one} config.add_class("two", "three") config.classes.should == %w{one two three} end it "should tag itself with passed class names" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.tags.should == %w{one} end end describe "when extracting transobjects" do def mkscope @node = Puppet::Node.new("mynode") @compiler = Puppet::Parser::Compiler.new(@node) # XXX This is ridiculous. @compiler.send(:evaluate_main) @scope = @compiler.topscope end def mkresource(type, name) Puppet::Parser::Resource.new(type, name, :source => @source, :scope => @scope) end it "should fail if no 'main' stage can be found" do lambda { Puppet::Resource::Catalog.new("mynode").extract }.should raise_error(Puppet::DevError) end it "should warn if any non-main stages are present" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @source = mock 'source' main = mkresource("stage", "main") config.add_resource(main) other = mkresource("stage", "other") config.add_resource(other) Puppet.expects(:warning) config.extract end it "should always create a TransBucket for the 'main' stage" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @source = mock 'source' main = mkresource("stage", "main") config.add_resource(main) result = config.extract result.type.should == "Stage" result.name.should == "main" end # Now try it with a more complicated graph -- a three tier graph, each tier it "should transform arbitrarily deep graphs into isomorphic trees" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @scope.stubs(:tags).returns([]) @source = mock 'source' # Create our scopes. top = mkresource "stage", "main" config.add_resource top topbucket = [] topbucket.expects(:classes=).with([]) top.expects(:to_trans).returns(topbucket) topres = mkresource "file", "/top" topres.expects(:to_trans).returns(:topres) config.add_edge top, topres middle = mkresource "class", "middle" middle.expects(:to_trans).returns([]) config.add_edge top, middle midres = mkresource "file", "/mid" midres.expects(:to_trans).returns(:midres) config.add_edge middle, midres bottom = mkresource "class", "bottom" bottom.expects(:to_trans).returns([]) config.add_edge middle, bottom botres = mkresource "file", "/bot" botres.expects(:to_trans).returns(:botres) config.add_edge bottom, botres toparray = config.extract # This is annoying; it should look like: # [[[:botres], :midres], :topres] # but we can't guarantee sort order. toparray.include?(:topres).should be_true midarray = toparray.find { |t| t.is_a?(Array) } midarray.include?(:midres).should be_true botarray = midarray.find { |t| t.is_a?(Array) } botarray.include?(:botres).should be_true end end describe " when converting to a Puppet::Resource catalog" do before do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @top = Puppet::TransObject.new 'top', "class" @topobject = Puppet::TransObject.new '/topobject', "file" @middle = Puppet::TransObject.new 'middle', "class" @middleobject = Puppet::TransObject.new '/middleobject', "file" @bottom = Puppet::TransObject.new 'bottom', "class" @bottomobject = Puppet::TransObject.new '/bottomobject', "file" @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] @original.add_resource(*@resources) @original.add_edge(@top, @topobject) @original.add_edge(@top, @middle) @original.add_edge(@middle, @middleobject) @original.add_edge(@middle, @bottom) @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_resource end it "should copy over the version" do @original.version = "foo" @original.to_resource.version.should == "foo" end it "should convert parser resources to plain resources" do resource = Puppet::Parser::Resource.new(:file, "foo", :scope => stub("scope", :environment => nil, :namespaces => nil), :source => stub("source")) catalog = Puppet::Resource::Catalog.new("whev") catalog.add_resource(resource) new = catalog.to_resource new.resource(:file, "foo").class.should == Puppet::Resource end it "should add all resources as Puppet::Resource instances" do @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Resource) } end it "should copy the tag list to the new catalog" do @catalog.tags.sort.should == @original.tags.sort end it "should copy the class list to the new catalog" do @catalog.classes.should == @original.classes end it "should duplicate the original edges" do @original.edges.each do |edge| @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true end end it "should set itself as the catalog for each converted resource" do @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } end end describe "when converting to a RAL catalog" do before do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @top = Puppet::Resource.new :class, 'top' @topobject = Puppet::Resource.new :file, @basepath+'/topobject' @middle = Puppet::Resource.new :class, 'middle' @middleobject = Puppet::Resource.new :file, @basepath+'/middleobject' @bottom = Puppet::Resource.new :class, 'bottom' @bottomobject = Puppet::Resource.new :file, @basepath+'/bottomobject' @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] @original.add_resource(*@resources) @original.add_edge(@top, @topobject) @original.add_edge(@top, @middle) @original.add_edge(@middle, @middleobject) @original.add_edge(@middle, @bottom) @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_ral end it "should add all resources as RAL instances" do @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } end it "should copy the tag list to the new catalog" do @catalog.tags.sort.should == @original.tags.sort end it "should copy the class list to the new catalog" do @catalog.classes.should == @original.classes end it "should duplicate the original edges" do @original.edges.each do |edge| @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true end end it "should set itself as the catalog for each converted resource" do @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } end # This tests #931. it "should not lose track of resources whose names vary" do changer = Puppet::TransObject.new 'changer', 'test' config = Puppet::Resource::Catalog.new('test') config.add_resource(changer) config.add_resource(@top) config.add_edge(@top, changer) resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil #changer is going to get duplicated as part of a fix for aliases 1094 changer.expects(:dup).returns(changer) changer.expects(:to_ral).returns(resource) newconfig = nil proc { @catalog = config.to_ral }.should_not raise_error @catalog.resource("Test[changer2]").should equal(resource) end after do # Remove all resource instances. @catalog.clear(true) end end describe "when filtering" do before :each do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @r1 = stub_everything 'r1', :ref => "File[/a]" @r1.stubs(:respond_to?).with(:ref).returns(true) @r1.stubs(:dup).returns(@r1) @r1.stubs(:is_a?).returns(Puppet::Resource).returns(true) @r2 = stub_everything 'r2', :ref => "File[/b]" @r2.stubs(:respond_to?).with(:ref).returns(true) @r2.stubs(:dup).returns(@r2) @r2.stubs(:is_a?).returns(Puppet::Resource).returns(true) @resources = [@r1,@r2] @original.add_resource(@r1,@r2) end it "should transform the catalog to a resource catalog" do @original.expects(:to_catalog).with { |h,b| h == :to_resource } @original.filter end it "should scan each catalog resource in turn and apply filtering block" do @resources.each { |r| r.expects(:test?) } @original.filter do |r| r.test? end end it "should filter out resources which produce true when the filter block is evaluated" do @original.filter do |r| r == @r1 end.resource("File[/a]").should be_nil end it "should not consider edges against resources that were filtered out" do @original.add_edge(@r1,@r2) @original.filter do |r| r == @r1 end.edge?(@r1,@r2).should_not be end end describe "when functioning as a resource container" do before do @catalog = Puppet::Resource::Catalog.new("host") @one = Puppet::Type.type(:notify).new :name => "one" @two = Puppet::Type.type(:notify).new :name => "two" @dupe = Puppet::Type.type(:notify).new :name => "one" end it "should provide a method to add one or more resources" do @catalog.add_resource @one, @two @catalog.resource(@one.ref).should equal(@one) @catalog.resource(@two.ref).should equal(@two) end it "should add resources to the relationship graph if it exists" do relgraph = @catalog.relationship_graph @catalog.add_resource @one relgraph.should be_vertex(@one) end it "should set itself as the resource's catalog if it is not a relationship graph" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should make all vertices available by resource reference" do @catalog.add_resource(@one) @catalog.resource(@one.ref).should equal(@one) @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) end it "should canonize how resources are referred to during retrieval when both type and title are provided" do @catalog.add_resource(@one) @catalog.resource("notify", "one").should equal(@one) end it "should canonize how resources are referred to during retrieval when just the title is provided" do @catalog.add_resource(@one) @catalog.resource("notify[one]", nil).should equal(@one) end it "should not allow two resources with the same resource reference" do @catalog.add_resource(@one) proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should not store objects that do not respond to :ref" do proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) end it "should remove all resources when asked" do @catalog.add_resource @one @catalog.add_resource @two @one.expects :remove @two.expects :remove @catalog.clear(true) end it "should support a mechanism for finishing resources" do @one.expects :finish @two.expects :finish @catalog.add_resource @one @catalog.add_resource @two @catalog.finalize end it "should make default resources when finalizing" do @catalog.expects(:make_default_resources) @catalog.finalize end it "should add default resources to the catalog upon creation" do @catalog.make_default_resources @catalog.resource(:schedule, "daily").should_not be_nil end it "should optionally support an initialization block and should finalize after such blocks" do @one.expects :finish @two.expects :finish config = Puppet::Resource::Catalog.new("host") do |conf| conf.add_resource @one conf.add_resource @two end end it "should inform the resource that it is the resource's catalog" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should be able to find resources by reference" do @catalog.add_resource @one @catalog.resource(@one.ref).should equal(@one) end it "should be able to find resources by reference or by type/title tuple" do @catalog.add_resource @one @catalog.resource("notify", "one").should equal(@one) end it "should have a mechanism for removing resources" do @catalog.add_resource @one @one.expects :remove @catalog.remove_resource(@one) @catalog.resource(@one.ref).should be_nil @catalog.vertex?(@one).should be_false end it "should have a method for creating aliases for resources" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should ignore conflicting aliases that point to the aliased resource" do @catalog.alias(@one, "other") lambda { @catalog.alias(@one, "other") }.should_not raise_error end it "should create aliases for resources isomorphic resources whose names do not match their titles" do resource = Puppet::Type::File.new(:title => "testing", :path => @basepath+"/something") @catalog.add_resource(resource) @catalog.resource(:file, @basepath+"/something").should equal(resource) end - it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do + it "should not create aliases for resources non-isomorphic resources whose names do not match their titles", :fails_on_windows => true do resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) @catalog.add_resource(resource) # Yay, I've already got a 'should' method @catalog.resource(:exec, "echo").object_id.should == nil.object_id end # This test is the same as the previous, but the behaviour should be explicit. it "should alias using the class name from the resource reference, not the resource class name" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should ignore conflicting aliases that point to the aliased resource" do @catalog.alias(@one, "other") lambda { @catalog.alias(@one, "other") }.should_not raise_error end it "should fail to add an alias if the aliased name already exists" do @catalog.add_resource @one proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) end it "should not fail when a resource has duplicate aliases created" do @catalog.add_resource @one proc { @catalog.alias @one, "one" }.should_not raise_error end it "should not create aliases that point back to the resource" do @catalog.alias(@one, "one") @catalog.resource(:notify, "one").should be_nil end it "should be able to look resources up by their aliases" do @catalog.add_resource @one @catalog.alias @one, "two" @catalog.resource(:notify, "two").should equal(@one) end it "should remove resource aliases when the target resource is removed" do @catalog.add_resource @one @catalog.alias(@one, "other") @one.expects :remove @catalog.remove_resource(@one) @catalog.resource("notify", "other").should be_nil end it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(true) @catalog.add_resource(resource) @catalog.resource(:file, "other").should equal(resource) @catalog.resource(:file, @basepath+"/something").ref.should == resource.ref end it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => @basepath+"/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(false) @catalog.add_resource(resource) @catalog.resource(:file, resource.title).should equal(resource) # We can't use .should here, because the resources respond to that method. raise "Aliased non-isomorphic resource" if @catalog.resource(:file, resource.name) end it "should provide a method to create additional resources that also registers the resource" do args = {:name => "/yay", :ensure => :file} resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" Puppet::Type.type(:file).expects(:new).with(args).returns(resource) @catalog.create_resource :file, args @catalog.resource("File[/yay]").should equal(resource) end end describe "when applying" do before :each do @catalog = Puppet::Resource::Catalog.new("host") @transaction = Puppet::Transaction.new(@catalog) Puppet::Transaction.stubs(:new).returns(@transaction) @transaction.stubs(:evaluate) @transaction.stubs(:add_times) @transaction.stubs(:for_network_device=) Puppet.settings.stubs(:use) end it "should create and evaluate a transaction" do @transaction.expects(:evaluate) @catalog.apply end it "should provide the catalog retrieval time to the transaction" do @catalog.retrieval_duration = 5 @transaction.expects(:add_times).with(:config_retrieval => 5) @catalog.apply end it "should use a retrieval time of 0 if none is set in the catalog" do @catalog.retrieval_duration = nil @transaction.expects(:add_times).with(:config_retrieval => 0) @catalog.apply end it "should return the transaction" do @catalog.apply.should equal(@transaction) end it "should yield the transaction if a block is provided" do @catalog.apply do |trans| trans.should equal(@transaction) end end it "should default to being a host catalog" do @catalog.host_config.should be_true end it "should be able to be set to a non-host_config" do @catalog.host_config = false @catalog.host_config.should be_false end it "should pass supplied tags on to the transaction" do @transaction.expects(:tags=).with(%w{one two}) @catalog.apply(:tags => %w{one two}) end it "should set ignoreschedules on the transaction if specified in apply()" do @transaction.expects(:ignoreschedules=).with(true) @catalog.apply(:ignoreschedules => true) end it "should expire cached data in the resources both before and after the transaction" do @catalog.expects(:expire).times(2) @catalog.apply end describe "host catalogs" do # super() doesn't work in the setup method for some reason before do @catalog.host_config = true Puppet::Util::Storage.stubs(:store) end it "should initialize the state database before applying a catalog" do Puppet::Util::Storage.expects(:load) # Short-circuit the apply, so we know we're loading before the transaction Puppet::Transaction.expects(:new).raises ArgumentError proc { @catalog.apply }.should raise_error(ArgumentError) end it "should sync the state database after applying" do Puppet::Util::Storage.expects(:store) @transaction.stubs :any_failed? => false @catalog.apply end after { Puppet.settings.clear } end describe "non-host catalogs" do before do @catalog.host_config = false end it "should never send reports" do Puppet[:report] = true Puppet[:summarize] = true @catalog.apply end it "should never modify the state database" do Puppet::Util::Storage.expects(:load).never Puppet::Util::Storage.expects(:store).never @catalog.apply end after { Puppet.settings.clear } end end describe "when creating a relationship graph" do before do Puppet::Type.type(:component) @catalog = Puppet::Resource::Catalog.new("host") @compone = Puppet::Type::Component.new :name => "one" @comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]" @file = Puppet::Type.type(:file) @one = @file.new :path => @basepath+"/one" @two = @file.new :path => @basepath+"/two" @sub = @file.new :path => @basepath+"/two/subdir" @catalog.add_edge @compone, @one @catalog.add_edge @comptwo, @two @three = @file.new :path => @basepath+"/three" @four = @file.new :path => @basepath+"/four", :require => "File[#{@basepath}/three]" @five = @file.new :path => @basepath+"/five" @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub @relationships = @catalog.relationship_graph end it "should be able to create a relationship graph" do @relationships.should be_instance_of(Puppet::SimpleGraph) end it "should not have any components" do @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil end it "should have all non-component resources from the catalog" do # The failures print out too much info, so i just do a class comparison @relationships.vertex?(@five).should be_true end it "should have all resource relationships set as edges" do @relationships.edge?(@three, @four).should be_true end it "should copy component relationships to all contained resources" do @relationships.path_between(@one, @two).should be end it "should add automatic relationships to the relationship graph" do @relationships.edge?(@two, @sub).should be_true end it "should get removed when the catalog is cleaned up" do @relationships.expects(:clear) @catalog.clear @catalog.instance_variable_get("@relationship_graph").should be_nil end it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).with(:relationships) graph.expects(:write_graph).with(:expanded_relationships) @catalog.host_config = true @catalog.relationship_graph end it "should not write graph files if the catalog is not a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).never @catalog.host_config = false @catalog.relationship_graph end it "should create a new relationship graph after clearing the old one" do @relationships.expects(:clear) @catalog.clear @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) end it "should remove removed resources from the relationship graph if it exists" do @catalog.remove_resource(@one) @catalog.relationship_graph.vertex?(@one).should be_false end end describe "when writing dot files" do before do @catalog = Puppet::Resource::Catalog.new("host") @name = :test @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") end it "should only write when it is a host catalog" do File.expects(:open).with(@file).never @catalog.host_config = false Puppet[:graph] = true @catalog.write_graph(@name) end after do Puppet.settings.clear end end describe "when indirecting" do before do @real_indirection = Puppet::Resource::Catalog.indirection @indirection = stub 'indirection', :name => :catalog Puppet::Util::Cacher.expire end it "should use the value of the 'catalog_terminus' setting to determine its terminus class" do # Puppet only checks the terminus setting the first time you ask # so this returns the object to the clean state # at the expense of making this test less pure Puppet::Resource::Catalog.indirection.reset_terminus_class Puppet.settings[:catalog_terminus] = "rest" Puppet::Resource::Catalog.indirection.terminus_class.should == :rest end it "should allow the terminus class to be set manually" do Puppet::Resource::Catalog.indirection.terminus_class = :rest Puppet::Resource::Catalog.indirection.terminus_class.should == :rest end after do Puppet::Util::Cacher.expire @real_indirection.reset_terminus_class end end describe "when converting to yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") end it "should be able to be dumped to yaml" do YAML.dump(@catalog).should be_instance_of(String) end end describe "when converting from yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") text = YAML.dump(@catalog) @newcatalog = YAML.load(text) end it "should get converted back to a catalog" do @newcatalog.should be_instance_of(Puppet::Resource::Catalog) end it "should have all vertices" do @newcatalog.vertex?("one").should be_true @newcatalog.vertex?("two").should be_true end it "should have all edges" do @newcatalog.edge?("one", "two").should be_true end end end describe Puppet::Resource::Catalog, "when converting to pson", :if => Puppet.features.pson? do before do @catalog = Puppet::Resource::Catalog.new("myhost") end def pson_output_should @catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its document_type to 'Catalog'" do pson_output_should { |hash| hash['document_type'] == "Catalog" } PSON.parse @catalog.to_pson end it "should set its data as a hash" do pson_output_should { |hash| hash['data'].is_a?(Hash) } PSON.parse @catalog.to_pson end [:name, :version, :tags, :classes].each do |param| it "should set its #{param} to the #{param} of the resource" do @catalog.send(param.to_s + "=", "testing") unless @catalog.send(param) pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) } PSON.parse @catalog.to_pson end end it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]" two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]" @catalog.add_resource(one) @catalog.add_resource(two) # TODO this should really guarantee sort order PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort end it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => 'Foo[one]' two = stub 'two', :to_pson_data_hash => "two_resource", :ref => 'Foo[two]' three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]' @catalog.add_edge(one, two) @catalog.add_edge(two, three) @catalog.edges_between(one, two )[0].expects(:to_pson_data_hash).returns "one_two_pson" @catalog.edges_between(two, three)[0].expects(:to_pson_data_hash).returns "two_three_pson" PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort end end describe Puppet::Resource::Catalog, "when converting from pson", :if => Puppet.features.pson? do def pson_result_should Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash } end before do @data = { 'name' => "myhost" } @pson = { 'document_type' => 'Puppet::Resource::Catalog', 'data' => @data, 'metadata' => {} } @catalog = Puppet::Resource::Catalog.new("myhost") Puppet::Resource::Catalog.stubs(:new).returns @catalog end it "should be extended with the PSON utility module" do Puppet::Resource::Catalog.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end it "should create it with the provided name" do Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog PSON.parse @pson.to_pson end it "should set the provided version on the catalog if one is set" do @data['version'] = 50 PSON.parse @pson.to_pson @catalog.version.should == @data['version'] end it "should set any provided tags on the catalog" do @data['tags'] = %w{one two} PSON.parse @pson.to_pson @catalog.tags.should == @data['tags'] end it "should set any provided classes on the catalog" do @data['classes'] = %w{one two} PSON.parse @pson.to_pson @catalog.classes.should == @data['classes'] end it 'should convert the resources list into resources and add each of them' do @data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")] @catalog.expects(:add_resource).times(2).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert resources even if they do not include "type" information' do @data['resources'] = [Puppet::Resource.new(:file, "/foo")] @data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}' @catalog.expects(:add_resource).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert the edges list into edges and add each of them' do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh") @data['edges'] = [one, two] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } @catalog.expects(:add_edge).with { |edge| edge.event == "two" } PSON.parse @pson.to_pson end it "should be able to convert relationships that do not include 'type' information" do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}" @data['edges'] = [one] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } PSON.parse @pson.to_pson end it "should set the source and target for each edge to the actual resource" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns("target_resource") @catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" } PSON.parse @pson.to_pson end it "should fail if the source resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns(nil) @catalog.stubs(:resource).with("target").returns("target_resource") lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end it "should fail if the target resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.stubs(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns(nil) lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end describe "#title_key_for_ref" do it "should parse a resource ref string into a pair" do @catalog.title_key_for_ref("Title[name]").should == ["Title", "name"] end it "should parse a resource ref string into a pair, even if there's a newline inside the name" do @catalog.title_key_for_ref("Title[na\nme]").should == ["Title", "na\nme"] end end end diff --git a/spec/unit/resource/status_spec.rb b/spec/unit/resource/status_spec.rb index e5a9291db..18e3359df 100755 --- a/spec/unit/resource/status_spec.rb +++ b/spec/unit/resource/status_spec.rb @@ -1,152 +1,154 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/resource/status' describe Puppet::Resource::Status do + include PuppetSpec::Files + before do - @resource = Puppet::Type.type(:file).new :path => "/my/file" + @resource = Puppet::Type.type(:file).new :path => make_absolute("/my/file") @status = Puppet::Resource::Status.new(@resource) end it "should compute type and title correctly" do @status.resource_type.should == "File" - @status.title.should == "/my/file" + @status.title.should == make_absolute("/my/file") end [:node, :file, :line, :current_values, :status, :evaluation_time].each do |attr| it "should support #{attr}" do @status.send(attr.to_s + "=", "foo") @status.send(attr).should == "foo" end end [:skipped, :failed, :restarted, :failed_to_restart, :changed, :out_of_sync, :scheduled].each do |attr| it "should support #{attr}" do @status.send(attr.to_s + "=", "foo") @status.send(attr).should == "foo" end it "should have a boolean method for determining whehter it was #{attr}" do @status.send(attr.to_s + "=", "foo") @status.should send("be_#{attr}") end end it "should accept a resource at initialization" do Puppet::Resource::Status.new(@resource).resource.should_not be_nil end it "should set its source description to the resource's path" do @resource.expects(:path).returns "/my/path" Puppet::Resource::Status.new(@resource).source_description.should == "/my/path" end [:file, :line].each do |attr| it "should copy the resource's #{attr}" do @resource.expects(attr).returns "foo" Puppet::Resource::Status.new(@resource).send(attr).should == "foo" end end it "should copy the resource's tags" do @resource.expects(:tags).returns %w{foo bar} Puppet::Resource::Status.new(@resource).tags.should == %w{foo bar} end it "should always convert the resource to a string" do @resource.expects(:to_s).returns "foo" Puppet::Resource::Status.new(@resource).resource.should == "foo" end it "should support tags" do Puppet::Resource::Status.ancestors.should include(Puppet::Util::Tagging) end it "should create a timestamp at its creation time" do @status.time.should be_instance_of(Time) end describe "when sending logs" do before do Puppet::Util::Log.stubs(:new) end it "should set the tags to the event tags" do Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } @status.stubs(:tags).returns %w{one two} @status.send_log :notice, "my message" end [:file, :line].each do |attr| it "should pass the #{attr}" do Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } @status.send(attr.to_s + "=", "my val") @status.send_log :notice, "my message" end end it "should use the source description as the source" do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "my source" } @status.stubs(:source_description).returns "my source" @status.send_log :notice, "my message" end end it "should support adding events" do event = Puppet::Transaction::Event.new(:name => :foobar) @status.add_event(event) @status.events.should == [event] end it "should use '<<' to add events" do event = Puppet::Transaction::Event.new(:name => :foobar) (@status << event).should equal(@status) @status.events.should == [event] end it "should count the number of successful events and set changed" do 3.times{ @status << Puppet::Transaction::Event.new(:status => 'success') } @status.change_count.should == 3 @status.changed.should == true @status.out_of_sync.should == true end it "should not start with any changes" do @status.change_count.should == 0 @status.changed.should == false @status.out_of_sync.should == false end it "should not treat failure, audit, or noop events as changed" do ['failure', 'audit', 'noop'].each do |s| @status << Puppet::Transaction::Event.new(:status => s) end @status.change_count.should == 0 @status.changed.should == false end it "should not treat audit events as out of sync" do @status << Puppet::Transaction::Event.new(:status => 'audit') @status.out_of_sync_count.should == 0 @status.out_of_sync.should == false end ['failure', 'noop', 'success'].each do |event_status| it "should treat #{event_status} events as out of sync" do 3.times do @status << Puppet::Transaction::Event.new(:status => event_status) end @status.out_of_sync_count.should == 3 @status.out_of_sync.should == true end end describe "When converting to YAML", :'fails_on_ruby_1.9.2' => true do it "should include only documented attributes" do @status.file = "/foo.rb" @status.line = 27 @status.evaluation_time = 2.7 @status.tags = %w{one two} @status.to_yaml_properties.should == Puppet::Resource::Status::YAML_ATTRIBUTES.sort end end end diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index 0485bc7aa..093532119 100755 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -1,872 +1,873 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/resource' describe Puppet::Resource do + include PuppetSpec::Files before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + @basepath = make_absolute("/somepath") end [:catalog, :file, :line].each do |attr| it "should have an #{attr} attribute" do resource = Puppet::Resource.new("file", "/my/file") resource.should respond_to(attr) resource.should respond_to(attr.to_s + "=") end end it "should have a :title attribute" do Puppet::Resource.new(:user, "foo").title.should == "foo" end it "should require the type and title" do lambda { Puppet::Resource.new }.should raise_error(ArgumentError) end it "should canonize types to capitalized strings" do Puppet::Resource.new(:user, "foo").type.should == "User" end it "should canonize qualified types so all strings are capitalized" do Puppet::Resource.new("foo::bar", "foo").type.should == "Foo::Bar" end it "should tag itself with its type" do Puppet::Resource.new("file", "/f").should be_tagged("file") end it "should tag itself with its title if the title is a valid tag" do Puppet::Resource.new("user", "bar").should be_tagged("bar") end it "should not tag itself with its title if the title is a not valid tag" do Puppet::Resource.new("file", "/bar").should_not be_tagged("/bar") end it "should allow setting of attributes" do Puppet::Resource.new("file", "/bar", :file => "/foo").file.should == "/foo" Puppet::Resource.new("file", "/bar", :exported => true).should be_exported end it "should set its type to 'Class' and its title to the passed title if the passed type is :component and the title has no square brackets in it" do ref = Puppet::Resource.new(:component, "foo") ref.type.should == "Class" ref.title.should == "Foo" end it "should interpret the title as a reference and assign appropriately if the type is :component and the title contains square brackets" do ref = Puppet::Resource.new(:component, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should set the type to 'Class' if it is nil and the title contains no square brackets" do ref = Puppet::Resource.new(nil, "yay") ref.type.should == "Class" ref.title.should == "Yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[yay]") ref.type.should == "Foo::Bar" ref.title.should == "yay" end it "should interpret the title as a reference and assign appropriately if the type is nil and the title contains nested square brackets" do ref = Puppet::Resource.new(nil, "foo::bar[baz[yay]]") ref.type.should == "Foo::Bar" ref.title.should =="baz[yay]" end it "should interpret the type as a reference and assign appropriately if the title is nil and the type contains square brackets" do ref = Puppet::Resource.new("foo::bar[baz]") ref.type.should == "Foo::Bar" ref.title.should =="baz" end it "should be able to extract its information from a Puppet::Type instance" do ral = Puppet::Type.type(:file).new :path => @basepath+"/foo" ref = Puppet::Resource.new(ral) ref.type.should == "File" ref.title.should == @basepath+"/foo" end it "should fail if the title is nil and the type is not a valid resource reference string" do lambda { Puppet::Resource.new("foo") }.should raise_error(ArgumentError) end it 'should fail if strict is set and type does not exist' do lambda { Puppet::Resource.new('foo', 'title', {:strict=>true}) }.should raise_error(ArgumentError, 'Invalid resource type foo') end it 'should fail if strict is set and class does not exist' do lambda { Puppet::Resource.new('Class', 'foo', {:strict=>true}) }.should raise_error(ArgumentError, 'Could not find declared class foo') end it "should fail if the title is a hash and the type is not a valid resource reference string" do lambda { Puppet::Resource.new({:type => "foo", :title => "bar"}) }.should raise_error(ArgumentError, 'Puppet::Resource.new does not take a hash as the first argument. Did you mean ("foo", "bar") ?' ) end it "should be able to produce a backward-compatible reference array" do Puppet::Resource.new("foobar", "/f").to_trans_ref.should == %w{Foobar /f} end it "should be taggable" do Puppet::Resource.ancestors.should be_include(Puppet::Util::Tagging) end it "should have an 'exported' attribute" do resource = Puppet::Resource.new("file", "/f") resource.exported = true resource.exported.should == true resource.should be_exported end it "should support an environment attribute" do Puppet::Resource.new("file", "/my/file", :environment => :foo).environment.name.should == :foo end describe "and munging its type and title" do describe "when modeling a builtin resource" do it "should be able to find the resource type" do Puppet::Resource.new("file", "/my/file").resource_type.should equal(Puppet::Type.type(:file)) end it "should set its type to the capitalized type name" do Puppet::Resource.new("file", "/my/file").type.should == "File" end end describe "when modeling a defined resource" do describe "that exists" do before do @type = Puppet::Resource::Type.new(:definition, "foo::bar") Puppet::Node::Environment.new.known_resource_types.add @type end it "should set its type to the capitalized type name" do Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("foo::bar", "/my/file").resource_type.should equal(@type) end it "should set its title to the provided title" do Puppet::Resource.new("foo::bar", "/my/file").title.should == "/my/file" end end describe "that does not exist" do it "should set its resource type to the capitalized resource type name" do Puppet::Resource.new("foo::bar", "/my/file").type.should == "Foo::Bar" end end end describe "when modeling a node" do # Life's easier with nodes, because they can't be qualified. it "should set its type to 'Node' and its title to the provided title" do node = Puppet::Resource.new("node", "foo") node.type.should == "Node" node.title.should == "foo" end end describe "when modeling a class" do it "should set its type to 'Class'" do Puppet::Resource.new("class", "foo").type.should == "Class" end describe "that exists" do before do @type = Puppet::Resource::Type.new(:hostclass, "foo::bar") Puppet::Node::Environment.new.known_resource_types.add @type end it "should set its title to the capitalized, fully qualified resource type" do Puppet::Resource.new("class", "foo::bar").title.should == "Foo::Bar" end it "should be able to find the resource type" do Puppet::Resource.new("class", "foo::bar").resource_type.should equal(@type) end end describe "that does not exist" do it "should set its type to 'Class' and its title to the capitalized provided name" do klass = Puppet::Resource.new("class", "foo::bar") klass.type.should == "Class" klass.title.should == "Foo::Bar" end end describe "and its name is set to the empty string" do it "should set its title to :main" do Puppet::Resource.new("class", "").title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") Puppet::Node::Environment.new.known_resource_types.add @type Puppet::Resource.new("class", "").title.should == :main end end end describe "and its name is set to :main" do it "should set its title to :main" do Puppet::Resource.new("class", :main).title.should == :main end describe "and a class exists whose name is the empty string" do # this was a bit tough to track down it "should set its title to :main" do @type = Puppet::Resource::Type.new(:hostclass, "") Puppet::Node::Environment.new.known_resource_types.add @type Puppet::Resource.new("class", :main).title.should == :main end end end end end it "should return nil when looking up resource types that don't exist" do Puppet::Resource.new("foobar", "bar").resource_type.should be_nil end it "should not fail when an invalid parameter is used and strict mode is disabled" do type = Puppet::Resource::Type.new(:definition, "foobar") Puppet::Node::Environment.new.known_resource_types.add type resource = Puppet::Resource.new("foobar", "/my/file") resource[:yay] = true end it "should be considered equivalent to another resource if their type and title match and no parameters are set" do Puppet::Resource.new("file", "/f").should == Puppet::Resource.new("file", "/f") end it "should be considered equivalent to another resource if their type, title, and parameters are equal" do Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}).should == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to another resource if their type and title match but parameters are different" do Puppet::Resource.new("file", "/f", :parameters => {:fee => "baz"}).should_not == Puppet::Resource.new("file", "/f", :parameters => {:foo => "bar"}) end it "should not be considered equivalent to a non-resource" do Puppet::Resource.new("file", "/f").should_not == "foo" end it "should not be considered equivalent to another resource if their types do not match" do Puppet::Resource.new("file", "/f").should_not == Puppet::Resource.new("exec", "/f") end it "should not be considered equivalent to another resource if their titles do not match" do Puppet::Resource.new("file", "/foo").should_not == Puppet::Resource.new("file", "/f") end describe "when setting default parameters" do before do @scope = Puppet::Parser::Scope.new end it "should fail when asked to set default values and it is not a parser resource" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "default")}) ) resource = Puppet::Resource.new("default_param", "name") lambda { resource.set_default_parameters(@scope) }.should raise_error(Puppet::DevError) end it "should evaluate and set any default values when no value is provided" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => Puppet::Parser::Scope.new) resource.set_default_parameters(@scope) resource["a"].should == "a_default_value" end it "should skip attributes with no default value" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "a_default_value")}) ) resource = Puppet::Parser::Resource.new("no_default_param", "name", :scope => Puppet::Parser::Scope.new) lambda { resource.set_default_parameters(@scope) }.should_not raise_error end it "should return the list of default parameters set" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "default_param", :arguments => {"a" => Puppet::Parser::AST::String.new(:value => "a_default_value")}) ) resource = Puppet::Parser::Resource.new("default_param", "name", :scope => Puppet::Parser::Scope.new) resource.set_default_parameters(@scope).should == [:a] end end describe "when validating all required parameters are present" do it "should be able to validate that all required parameters are present" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "required_param", :arguments => {"a" => nil}) ) lambda { Puppet::Resource.new("required_param", "name").validate_complete }.should raise_error(Puppet::ParseError) end it "should not fail when all required parameters are present" do Puppet::Node::Environment.new.known_resource_types.add( Puppet::Resource::Type.new(:definition, "no_required_param") ) resource = Puppet::Resource.new("no_required_param", "name") resource["a"] = "meh" lambda { resource.validate_complete }.should_not raise_error end it "should not validate against builtin types" do lambda { Puppet::Resource.new("file", "/bar").validate_complete }.should_not raise_error end end describe "when referring to a resource with name canonicalization" do it "should canonicalize its own name" do res = Puppet::Resource.new("file", "/path/") res.uniqueness_key.should == ["/path"] res.ref.should == "File[/path/]" end end describe "when running in strict mode" do it "should be strict" do Puppet::Resource.new("file", "/path", :strict => true).should be_strict end it "should fail if invalid parameters are used" do lambda { Puppet::Resource.new("file", "/path", :strict => true, :parameters => {:nosuchparam => "bar"}) }.should raise_error end it "should fail if the resource type cannot be resolved" do lambda { Puppet::Resource.new("nosuchtype", "/path", :strict => true) }.should raise_error end end describe "when managing parameters" do before do @resource = Puppet::Resource.new("file", "/my/file") end it "should correctly detect when provided parameters are not valid for builtin types" do Puppet::Resource.new("file", "/my/file").should_not be_valid_parameter("foobar") end it "should correctly detect when provided parameters are valid for builtin types" do Puppet::Resource.new("file", "/my/file").should be_valid_parameter("mode") end it "should correctly detect when provided parameters are not valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar") Puppet::Node::Environment.new.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file").should_not be_valid_parameter("myparam") end it "should correctly detect when provided parameters are valid for defined resource types" do type = Puppet::Resource::Type.new(:definition, "foobar", :arguments => {"myparam" => nil}) Puppet::Node::Environment.new.known_resource_types.add type Puppet::Resource.new("foobar", "/my/file").should be_valid_parameter("myparam") end it "should allow setting and retrieving of parameters" do @resource[:foo] = "bar" @resource[:foo].should == "bar" end it "should allow setting of parameters at initialization" do Puppet::Resource.new("file", "/my/file", :parameters => {:foo => "bar"})[:foo].should == "bar" end it "should canonicalize retrieved parameter names to treat symbols and strings equivalently" do @resource[:foo] = "bar" @resource["foo"].should == "bar" end it "should canonicalize set parameter names to treat symbols and strings equivalently" do @resource["foo"] = "bar" @resource[:foo].should == "bar" end it "should set the namevar when asked to set the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:name] = "bob" resource[:myvar].should == "bob" end it "should return the namevar when asked to return the name" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource[:myvar] = "test" resource[:name].should == "test" end it "should be able to set the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" lambda { resource[:name] = "eh" }.should_not raise_error end it "should be able to return the name for non-builtin types" do resource = Puppet::Resource.new(:foo, "bar") resource[:name] = "eh" resource[:name].should == "eh" end it "should be able to iterate over parameters" do @resource[:foo] = "bar" @resource[:fee] = "bare" params = {} @resource.each do |key, value| params[key] = value end params.should == {:foo => "bar", :fee => "bare"} end it "should include Enumerable" do @resource.class.ancestors.should be_include(Enumerable) end it "should have a method for testing whether a parameter is included" do @resource[:foo] = "bar" @resource.should be_has_key(:foo) @resource.should_not be_has_key(:eh) end it "should have a method for providing the list of parameters" do @resource[:foo] = "bar" @resource[:bar] = "foo" keys = @resource.keys keys.should be_include(:foo) keys.should be_include(:bar) end it "should have a method for providing the number of parameters" do @resource[:foo] = "bar" @resource.length.should == 1 end it "should have a method for deleting parameters" do @resource[:foo] = "bar" @resource.delete(:foo) @resource[:foo].should be_nil end it "should have a method for testing whether the parameter list is empty" do @resource.should be_empty @resource[:foo] = "bar" @resource.should_not be_empty end it "should be able to produce a hash of all existing parameters" do @resource[:foo] = "bar" @resource[:fee] = "yay" hash = @resource.to_hash hash[:foo].should == "bar" hash[:fee].should == "yay" end it "should not provide direct access to the internal parameters hash when producing a hash" do hash = @resource.to_hash hash[:foo] = "bar" @resource[:foo].should be_nil end it "should use the title as the namevar to the hash if no namevar is present" do resource = Puppet::Resource.new("user", "bob") Puppet::Type.type(:user).stubs(:key_attributes).returns [:myvar] resource.to_hash[:myvar].should == "bob" end it "should set :name to the title if :name is not present for non-builtin types" do krt = Puppet::Resource::TypeCollection.new("myenv") krt.add Puppet::Resource::Type.new(:definition, :foo) resource = Puppet::Resource.new :foo, "bar" resource.stubs(:known_resource_types).returns krt resource.to_hash[:name].should == "bar" end end describe "when serializing" do before do @resource = Puppet::Resource.new("file", "/my/file") @resource["one"] = "test" @resource["two"] = "other" end it "should be able to be dumped to yaml" do proc { YAML.dump(@resource) }.should_not raise_error end it "should produce an equivalent yaml object" do text = YAML.dump(@resource) newresource = YAML.load(text) newresource.title.should == @resource.title newresource.type.should == @resource.type %w{one two}.each do |param| newresource[param].should == @resource[param] end end end describe "when loading 0.25.x storedconfigs YAML" do before :each do @old_storedconfig_yaml = %q{--- !ruby/object:Puppet::Resource::Reference builtin_type: title: /tmp/bar type: File } end it "should deserialize a Puppet::Resource::Reference without exceptions" do lambda { YAML.load(@old_storedconfig_yaml) }.should_not raise_error end it "should deserialize as a Puppet::Resource::Reference as a Puppet::Resource" do YAML.load(@old_storedconfig_yaml).class.should == Puppet::Resource end it "should to_hash properly" do YAML.load(@old_storedconfig_yaml).to_hash.should == { :path => "/tmp/bar" } end end describe "when converting to a RAL resource" do it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do resource = Puppet::Resource.new("file", @basepath+"/my/file") result = resource.to_ral result.should be_instance_of(Puppet::Type.type(:file)) result[:path].should == @basepath+"/my/file" end it "should convert to a component instance if the resource type is not of a builtin type" do resource = Puppet::Resource.new("foobar", "somename") result = resource.to_ral result.should be_instance_of(Puppet::Type.type(:component)) result.title.should == "Foobar[somename]" end end it "should be able to convert itself to Puppet code" do Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_manifest) end describe "when converting to puppet code" do before do @resource = Puppet::Resource.new("one::two", "/my/file", :parameters => { :noop => true, :foo => %w{one two}, :ensure => 'present', } ) end it "should align, sort and add trailing commas to attributes with ensure first", :'fails_on_ruby_1.9.2' => true do @resource.to_manifest.should == <<-HEREDOC.gsub(/^\s{8}/, '').gsub(/\n$/, '') one::two { '/my/file': ensure => 'present', foo => ['one', 'two'], noop => 'true', } HEREDOC end end it "should be able to convert itself to a TransObject instance" do Puppet::Resource.new("one::two", "/my/file").should respond_to(:to_trans) end describe "when converting to a TransObject" do describe "and the resource is not an instance of a builtin type" do before do @resource = Puppet::Resource.new("foo", "bar") end it "should return a simple TransBucket if it is not an instance of a builtin type" do bucket = @resource.to_trans bucket.should be_instance_of(Puppet::TransBucket) bucket.type.should == @resource.type bucket.name.should == @resource.title end it "should return a simple TransBucket if it is a stage" do @resource = Puppet::Resource.new("stage", "bar") bucket = @resource.to_trans bucket.should be_instance_of(Puppet::TransBucket) bucket.type.should == @resource.type bucket.name.should == @resource.title end it "should copy over the resource's file" do @resource.file = "/foo/bar" @resource.to_trans.file.should == "/foo/bar" end it "should copy over the resource's line" do @resource.line = 50 @resource.to_trans.line.should == 50 end end describe "and the resource is an instance of a builtin type" do before do @resource = Puppet::Resource.new("file", "bar") end it "should return a TransObject if it is an instance of a builtin resource type" do trans = @resource.to_trans trans.should be_instance_of(Puppet::TransObject) trans.type.should == "file" trans.name.should == @resource.title end it "should copy over the resource's file" do @resource.file = "/foo/bar" @resource.to_trans.file.should == "/foo/bar" end it "should copy over the resource's line" do @resource.line = 50 @resource.to_trans.line.should == 50 end # Only TransObjects support tags, annoyingly it "should copy over the resource's tags" do @resource.tag "foo" @resource.to_trans.tags.should == @resource.tags end it "should copy the resource's parameters into the transobject and convert the parameter name to a string" do @resource[:foo] = "bar" @resource.to_trans["foo"].should == "bar" end it "should be able to copy arrays of values" do @resource[:foo] = %w{yay fee} @resource.to_trans["foo"].should == %w{yay fee} end it "should reduce single-value arrays to just a value" do @resource[:foo] = %w{yay} @resource.to_trans["foo"].should == "yay" end it "should convert resource references into the backward-compatible form" do @resource[:foo] = Puppet::Resource.new(:file, "/f") @resource.to_trans["foo"].should == %w{File /f} end it "should convert resource references into the backward-compatible form even when within arrays" do @resource[:foo] = ["a", Puppet::Resource.new(:file, "/f")] @resource.to_trans["foo"].should == ["a", %w{File /f}] end end end describe "when converting to pson", :if => Puppet.features.pson? do def pson_output_should @resource.class.expects(:pson_create).with { |hash| yield hash } end it "should include the pson util module" do Puppet::Resource.singleton_class.ancestors.should be_include(Puppet::Util::Pson) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its type to the provided type" do Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_pson(PSON.parse(Puppet::Resource.new("File", "/foo").to_pson)).title.should == "/foo" end it "should include all tags from the resource" do resource = Puppet::Resource.new("File", "/foo") resource.tag("yay") Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).tags.should == resource.tags end it "should include the file if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.file = "/my/file" Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).file.should == "/my/file" end it "should include the line if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.line = 50 Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).line.should == 50 end it "should include the 'exported' value if one is set" do resource = Puppet::Resource.new("File", "/foo") resource.exported = true Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_true end it "should set 'exported' to false if no value is set" do resource = Puppet::Resource.new("File", "/foo") Puppet::Resource.from_pson(PSON.parse(resource.to_pson)).exported.should be_false end it "should set all of its parameters as the 'parameters' entry" do resource = Puppet::Resource.new("File", "/foo") resource[:foo] = %w{bar eh} resource[:fee] = %w{baz} result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result["foo"].should == %w{bar eh} result["fee"].should == %w{baz} end it "should serialize relationships as reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = Puppet::Resource.new("File", "/bar") result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result[:requires].should == "File[/bar]" end it "should serialize multiple relationships as arrays of reference strings" do resource = Puppet::Resource.new("File", "/foo") resource[:requires] = [Puppet::Resource.new("File", "/bar"), Puppet::Resource.new("File", "/baz")] result = Puppet::Resource.from_pson(PSON.parse(resource.to_pson)) result[:requires].should == [ "File[/bar]", "File[/baz]" ] end end describe "when converting from pson", :if => Puppet.features.pson? do def pson_result_should Puppet::Resource.expects(:new).with { |hash| yield hash } end before do @data = { 'type' => "file", 'title' => @basepath+"/yay", } end it "should set its type to the provided type" do Puppet::Resource.from_pson(@data).type.should == "File" end it "should set its title to the provided title" do Puppet::Resource.from_pson(@data).title.should == @basepath+"/yay" end it "should tag the resource with any provided tags" do @data['tags'] = %w{foo bar} resource = Puppet::Resource.from_pson(@data) resource.tags.should be_include("foo") resource.tags.should be_include("bar") end it "should set its file to the provided file" do @data['file'] = "/foo/bar" Puppet::Resource.from_pson(@data).file.should == "/foo/bar" end it "should set its line to the provided line" do @data['line'] = 50 Puppet::Resource.from_pson(@data).line.should == 50 end it "should 'exported' to true if set in the pson data" do @data['exported'] = true Puppet::Resource.from_pson(@data).exported.should be_true end it "should 'exported' to false if not set in the pson data" do Puppet::Resource.from_pson(@data).exported.should be_false end it "should fail if no title is provided" do @data.delete('title') lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) end it "should fail if no type is provided" do @data.delete('type') lambda { Puppet::Resource.from_pson(@data) }.should raise_error(ArgumentError) end it "should set each of the provided parameters" do @data['parameters'] = {'foo' => %w{one two}, 'fee' => %w{three four}} resource = Puppet::Resource.from_pson(@data) resource['foo'].should == %w{one two} resource['fee'].should == %w{three four} end it "should convert single-value array parameters to normal values" do @data['parameters'] = {'foo' => %w{one}} resource = Puppet::Resource.from_pson(@data) resource['foo'].should == %w{one} end end describe "it should implement to_resource" do resource = Puppet::Resource.new("file", "/my/file") resource.to_resource.should == resource end describe "because it is an indirector model" do it "should include Puppet::Indirector" do Puppet::Resource.should be_is_a(Puppet::Indirector) end it "should have a default terminus" do Puppet::Resource.indirection.terminus_class.should == :ral end it "should have a name" do Puppet::Resource.new("file", "/my/file").name.should == "File//my/file" end end describe "when resolving resources with a catalog" do it "should resolve all resources using the catalog" do catalog = mock 'catalog' resource = Puppet::Resource.new("foo::bar", "yay") resource.catalog = catalog catalog.expects(:resource).with("Foo::Bar[yay]").returns(:myresource) resource.resolve.should == :myresource end end describe "when generating the uniqueness key" do it "should include all of the key_attributes in alphabetical order by attribute name" do Puppet::Type.type(:file).stubs(:key_attributes).returns [:myvar, :owner, :path] Puppet::Type.type(:file).stubs(:title_patterns).returns( [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] ) res = Puppet::Resource.new("file", "/my/file", :parameters => {:owner => 'root', :content => 'hello'}) res.uniqueness_key.should == [ nil, 'root', '/my/file'] end end end diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb index c2d9690e6..e1680941f 100755 --- a/spec/unit/ssl/host_spec.rb +++ b/spec/unit/ssl/host_spec.rb @@ -1,793 +1,793 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/ssl/host' require 'puppet/sslcertificates' require 'puppet/sslcertificates/ca' -describe Puppet::SSL::Host do +describe Puppet::SSL::Host, :fails_on_windows => true do before do Puppet::SSL::Host.indirection.terminus_class = :file @host = Puppet::SSL::Host.new("myname") end after do # Cleaned out any cached localhost instance. Puppet::Util::Cacher.expire Puppet::SSL::Host.ca_location = :none end it "should use any provided name as its name" do @host.name.should == "myname" end it "should retrieve its public key from its private key" do realkey = mock 'realkey' key = stub 'key', :content => realkey Puppet::SSL::Key.indirection.stubs(:find).returns(key) pubkey = mock 'public_key' realkey.expects(:public_key).returns pubkey @host.public_key.should equal(pubkey) end it "should default to being a non-ca host" do @host.ca?.should be_false end it "should be a ca host if its name matches the CA_NAME" do Puppet::SSL::Host.stubs(:ca_name).returns "yayca" Puppet::SSL::Host.new("yayca").should be_ca end it "should have a method for determining the CA location" do Puppet::SSL::Host.should respond_to(:ca_location) end it "should have a method for specifying the CA location" do Puppet::SSL::Host.should respond_to(:ca_location=) end it "should have a method for retrieving the default ssl host" do Puppet::SSL::Host.should respond_to(:ca_location=) end it "should have a method for producing an instance to manage the local host's keys" do Puppet::SSL::Host.should respond_to(:localhost) end it "should generate the certificate for the localhost instance if no certificate is available" do host = stub 'host', :key => nil Puppet::SSL::Host.expects(:new).returns host host.expects(:certificate).returns nil host.expects(:generate) Puppet::SSL::Host.localhost.should equal(host) end it "should always read the key for the localhost instance in from disk" do host = stub 'host', :certificate => "eh" Puppet::SSL::Host.expects(:new).returns host host.expects(:key) Puppet::SSL::Host.localhost end it "should cache the localhost instance" do host = stub 'host', :certificate => "eh", :key => 'foo' Puppet::SSL::Host.expects(:new).once.returns host Puppet::SSL::Host.localhost.should == Puppet::SSL::Host.localhost end it "should be able to expire the cached instance" do one = stub 'host1', :certificate => "eh", :key => 'foo' two = stub 'host2', :certificate => "eh", :key => 'foo' Puppet::SSL::Host.expects(:new).times(2).returns(one).then.returns(two) Puppet::SSL::Host.localhost.should equal(one) Puppet::Util::Cacher.expire Puppet::SSL::Host.localhost.should equal(two) end it "should be able to verify its certificate matches its key" do Puppet::SSL::Host.new("foo").should respond_to(:certificate_matches_key?) end it "should consider the certificate invalid if it cannot find a key" do host = Puppet::SSL::Host.new("foo") host.expects(:key).returns nil host.should_not be_certificate_matches_key end it "should consider the certificate invalid if it cannot find a certificate" do host = Puppet::SSL::Host.new("foo") host.expects(:key).returns mock("key") host.expects(:certificate).returns nil host.should_not be_certificate_matches_key end it "should consider the certificate invalid if the SSL certificate's key verification fails" do host = Puppet::SSL::Host.new("foo") key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' certificate = mock 'cert', :content => sslcert host.stubs(:key).returns key host.stubs(:certificate).returns certificate sslcert.expects(:check_private_key).with("private_key").returns false host.should_not be_certificate_matches_key end it "should consider the certificate valid if the SSL certificate's key verification succeeds" do host = Puppet::SSL::Host.new("foo") key = mock 'key', :content => "private_key" sslcert = mock 'sslcert' certificate = mock 'cert', :content => sslcert host.stubs(:key).returns key host.stubs(:certificate).returns certificate sslcert.expects(:check_private_key).with("private_key").returns true host.should be_certificate_matches_key end describe "when specifying the CA location" do it "should support the location ':local'" do lambda { Puppet::SSL::Host.ca_location = :local }.should_not raise_error end it "should support the location ':remote'" do lambda { Puppet::SSL::Host.ca_location = :remote }.should_not raise_error end it "should support the location ':none'" do lambda { Puppet::SSL::Host.ca_location = :none }.should_not raise_error end it "should support the location ':only'" do lambda { Puppet::SSL::Host.ca_location = :only }.should_not raise_error end it "should not support other modes" do lambda { Puppet::SSL::Host.ca_location = :whatever }.should raise_error(ArgumentError) end describe "as 'local'" do before do Puppet::SSL::Host.ca_location = :local end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Certificate.indirection.cache_class.should == :file Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file end it "should set the terminus class for Key and Host as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file Puppet::SSL::Host.indirection.terminus_class.should == :file end it "should set the terminus class for Certificate, CertificateRevocationList, and CertificateRequest as :ca" do Puppet::SSL::Certificate.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca end end describe "as 'remote'" do before do Puppet::SSL::Host.ca_location = :remote end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Certificate.indirection.cache_class.should == :file Puppet::SSL::CertificateRequest.indirection.cache_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.cache_class.should == :file end it "should set the terminus class for Key as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file end it "should set the terminus class for Host, Certificate, CertificateRevocationList, and CertificateRequest as :rest" do Puppet::SSL::Host.indirection.terminus_class.should == :rest Puppet::SSL::Certificate.indirection.terminus_class.should == :rest Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :rest Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :rest end end describe "as 'only'" do before do Puppet::SSL::Host.ca_location = :only end it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :ca" do Puppet::SSL::Key.indirection.terminus_class.should == :ca Puppet::SSL::Certificate.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :ca Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :ca end it "should set the cache class for Certificate, CertificateRevocationList, and CertificateRequest to nil" do Puppet::SSL::Certificate.indirection.cache_class.should be_nil Puppet::SSL::CertificateRequest.indirection.cache_class.should be_nil Puppet::SSL::CertificateRevocationList.indirection.cache_class.should be_nil end it "should set the terminus class for Host to :file" do Puppet::SSL::Host.indirection.terminus_class.should == :file end end describe "as 'none'" do before do Puppet::SSL::Host.ca_location = :none end it "should set the terminus class for Key, Certificate, CertificateRevocationList, and CertificateRequest as :file" do Puppet::SSL::Key.indirection.terminus_class.should == :file Puppet::SSL::Certificate.indirection.terminus_class.should == :file Puppet::SSL::CertificateRequest.indirection.terminus_class.should == :file Puppet::SSL::CertificateRevocationList.indirection.terminus_class.should == :file end it "should set the terminus class for Host to 'none'" do lambda { Puppet::SSL::Host.indirection.terminus_class }.should raise_error(Puppet::DevError) end end end it "should have a class method for destroying all files related to a given host" do Puppet::SSL::Host.should respond_to(:destroy) end describe "when destroying a host's SSL files" do before do Puppet::SSL::Key.indirection.stubs(:destroy).returns false Puppet::SSL::Certificate.indirection.stubs(:destroy).returns false Puppet::SSL::CertificateRequest.indirection.stubs(:destroy).returns false end it "should destroy its certificate, certificate request, and key" do Puppet::SSL::Key.indirection.expects(:destroy).with("myhost") Puppet::SSL::Certificate.indirection.expects(:destroy).with("myhost") Puppet::SSL::CertificateRequest.indirection.expects(:destroy).with("myhost") Puppet::SSL::Host.destroy("myhost") end it "should return true if any of the classes returned true" do Puppet::SSL::Certificate.indirection.expects(:destroy).with("myhost").returns true Puppet::SSL::Host.destroy("myhost").should be_true end it "should report that nothing was deleted if none of the classes returned true" do Puppet::SSL::Host.destroy("myhost").should == "Nothing was deleted" end end describe "when initializing" do it "should default its name to the :certname setting" do Puppet.settings.expects(:value).with(:certname).returns "myname" Puppet::SSL::Host.new.name.should == "myname" end it "should downcase a passed in name" do Puppet::SSL::Host.new("Host.Domain.Com").name.should == "host.domain.com" end it "should downcase the certname if it's used" do Puppet.settings.expects(:value).with(:certname).returns "Host.Domain.Com" Puppet::SSL::Host.new.name.should == "host.domain.com" end it "should indicate that it is a CA host if its name matches the ca_name constant" do Puppet::SSL::Host.stubs(:ca_name).returns "myca" Puppet::SSL::Host.new("myca").should be_ca end end describe "when managing its private key" do before do @realkey = "mykey" @key = Puppet::SSL::Key.new("mykey") @key.content = @realkey end it "should return nil if the key is not set and cannot be found" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(nil) @host.key.should be_nil end it "should find the key in the Key class and return the Puppet instance" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(@key) @host.key.should equal(@key) end it "should be able to generate and save a new key" do Puppet::SSL::Key.expects(:new).with("myname").returns(@key) @key.expects(:generate) Puppet::SSL::Key.indirection.expects(:save) @host.generate_key.should be_true @host.key.should equal(@key) end it "should not retain keys that could not be saved" do Puppet::SSL::Key.expects(:new).with("myname").returns(@key) @key.stubs(:generate) Puppet::SSL::Key.indirection.expects(:save).raises "eh" lambda { @host.generate_key }.should raise_error @host.key.should be_nil end it "should return any previously found key without requerying" do Puppet::SSL::Key.indirection.expects(:find).with("myname").returns(@key).once @host.key.should equal(@key) @host.key.should equal(@key) end end describe "when managing its certificate request" do before do @realrequest = "real request" @request = Puppet::SSL::CertificateRequest.new("myname") @request.content = @realrequest end it "should return nil if the key is not set and cannot be found" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns(nil) @host.certificate_request.should be_nil end it "should find the request in the Key class and return it and return the Puppet SSL request" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns @request @host.certificate_request.should equal(@request) end it "should generate a new key when generating the cert request if no key exists" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.expects(:key).times(2).returns(nil).then.returns(key) @host.expects(:generate_key).returns(key) @request.stubs(:generate) Puppet::SSL::CertificateRequest.indirection.stubs(:save) @host.generate_certificate_request end it "should be able to generate and save a new request using the private key" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.stubs(:key).returns(key) @request.expects(:generate).with("mycontent") Puppet::SSL::CertificateRequest.indirection.expects(:save).with(@request) @host.generate_certificate_request.should be_true @host.certificate_request.should equal(@request) end it "should return any previously found request without requerying" do Puppet::SSL::CertificateRequest.indirection.expects(:find).with("myname").returns(@request).once @host.certificate_request.should equal(@request) @host.certificate_request.should equal(@request) end it "should not keep its certificate request in memory if the request cannot be saved" do Puppet::SSL::CertificateRequest.expects(:new).with("myname").returns @request key = stub 'key', :public_key => mock("public_key"), :content => "mycontent" @host.stubs(:key).returns(key) @request.stubs(:generate) @request.stubs(:name).returns("myname") terminus = stub 'terminus' Puppet::SSL::CertificateRequest.indirection.expects(:prepare).returns(terminus) terminus.expects(:save).with { |req| req.instance == @request && req.key == "myname" }.raises "eh" lambda { @host.generate_certificate_request }.should raise_error @host.instance_eval { @certificate_request }.should be_nil end end describe "when managing its certificate" do before do @realcert = mock 'certificate' @cert = stub 'cert', :content => @realcert @host.stubs(:key).returns mock("key") @host.stubs(:certificate_matches_key?).returns true end it "should find the CA certificate if it does not have a certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.stubs(:find).with("myname").returns @cert @host.certificate end it "should not find the CA certificate if it is the CA host" do @host.expects(:ca?).returns true Puppet::SSL::Certificate.indirection.stubs(:find) Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).never @host.certificate end it "should return nil if it cannot find a CA certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns nil Puppet::SSL::Certificate.indirection.expects(:find).with("myname").never @host.certificate.should be_nil end it "should find the key if it does not have one" do Puppet::SSL::Certificate.indirection.stubs(:find) @host.expects(:key).returns mock("key") @host.certificate end it "should generate the key if one cannot be found" do Puppet::SSL::Certificate.indirection.stubs(:find) @host.expects(:key).returns nil @host.expects(:generate_key) @host.certificate end it "should find the certificate in the Certificate class and return the Puppet certificate instance" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns @cert @host.certificate.should equal(@cert) end it "should fail if the found certificate does not match the private key" do @host.expects(:certificate_matches_key?).returns false Puppet::SSL::Certificate.indirection.stubs(:find).returns @cert lambda { @host.certificate }.should raise_error(Puppet::Error) end it "should return any previously found certificate" do Puppet::SSL::Certificate.indirection.expects(:find).with(Puppet::SSL::CA_NAME).returns mock("cacert") Puppet::SSL::Certificate.indirection.expects(:find).with("myname").returns(@cert).once @host.certificate.should equal(@cert) @host.certificate.should equal(@cert) end end it "should have a method for listing certificate hosts" do Puppet::SSL::Host.should respond_to(:search) end describe "when listing certificate hosts" do it "should default to listing all clients with any file types" do Puppet::SSL::Key.indirection.expects(:search).returns [] Puppet::SSL::Certificate.indirection.expects(:search).returns [] Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [] Puppet::SSL::Host.search end it "should be able to list only clients with a key" do Puppet::SSL::Key.indirection.expects(:search).returns [] Puppet::SSL::Certificate.indirection.expects(:search).never Puppet::SSL::CertificateRequest.indirection.expects(:search).never Puppet::SSL::Host.search :for => Puppet::SSL::Key end it "should be able to list only clients with a certificate" do Puppet::SSL::Key.indirection.expects(:search).never Puppet::SSL::Certificate.indirection.expects(:search).returns [] Puppet::SSL::CertificateRequest.indirection.expects(:search).never Puppet::SSL::Host.search :for => Puppet::SSL::Certificate end it "should be able to list only clients with a certificate request" do Puppet::SSL::Key.indirection.expects(:search).never Puppet::SSL::Certificate.indirection.expects(:search).never Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [] Puppet::SSL::Host.search :for => Puppet::SSL::CertificateRequest end it "should return a Host instance created with the name of each found instance", :'fails_on_ruby_1.9.2' => true do key = stub 'key', :name => "key" cert = stub 'cert', :name => "cert" csr = stub 'csr', :name => "csr" Puppet::SSL::Key.indirection.expects(:search).returns [key] Puppet::SSL::Certificate.indirection.expects(:search).returns [cert] Puppet::SSL::CertificateRequest.indirection.expects(:search).returns [csr] returned = [] %w{key cert csr}.each do |name| result = mock(name) returned << result Puppet::SSL::Host.expects(:new).with(name).returns result end result = Puppet::SSL::Host.search returned.each do |r| result.should be_include(r) end end end it "should have a method for generating all necessary files" do Puppet::SSL::Host.new("me").should respond_to(:generate) end describe "when generating files" do before do @host = Puppet::SSL::Host.new("me") @host.stubs(:generate_key) @host.stubs(:generate_certificate_request) end it "should generate a key if one is not present" do @host.stubs(:key).returns nil @host.expects(:generate_key) @host.generate end it "should generate a certificate request if one is not present" do @host.expects(:certificate_request).returns nil @host.expects(:generate_certificate_request) @host.generate end describe "and it can create a certificate authority" do before do @ca = mock 'ca' Puppet::SSL::CertificateAuthority.stubs(:instance).returns @ca end it "should use the CA to sign its certificate request if it does not have a certificate" do @host.expects(:certificate).returns nil @ca.expects(:sign).with(@host.name) @host.generate end end describe "and it cannot create a certificate authority" do before do Puppet::SSL::CertificateAuthority.stubs(:instance).returns nil end it "should seek its certificate" do @host.expects(:certificate) @host.generate end end end it "should have a method for creating an SSL store" do Puppet::SSL::Host.new("me").should respond_to(:ssl_store) end it "should always return the same store" do host = Puppet::SSL::Host.new("foo") store = mock 'store' store.stub_everything OpenSSL::X509::Store.expects(:new).returns store host.ssl_store.should equal(host.ssl_store) end describe "when creating an SSL store" do before do @host = Puppet::SSL::Host.new("me") @store = mock 'store' @store.stub_everything OpenSSL::X509::Store.stubs(:new).returns @store Puppet.settings.stubs(:value).with(:localcacert).returns "ssl_host_testing" Puppet::SSL::CertificateRevocationList.indirection.stubs(:find).returns(nil) end it "should accept a purpose" do @store.expects(:purpose=).with "my special purpose" @host.ssl_store("my special purpose") end it "should default to OpenSSL::X509::PURPOSE_ANY as the purpose" do @store.expects(:purpose=).with OpenSSL::X509::PURPOSE_ANY @host.ssl_store end it "should add the local CA cert file" do Puppet.settings.stubs(:value).with(:localcacert).returns "/ca/cert/file" @store.expects(:add_file).with "/ca/cert/file" @host.ssl_store end describe "and a CRL is available" do before do @crl = stub 'crl', :content => "real_crl" Puppet::SSL::CertificateRevocationList.indirection.stubs(:find).returns @crl Puppet.settings.stubs(:value).with(:certificate_revocation).returns true end it "should add the CRL" do @store.expects(:add_crl).with "real_crl" @host.ssl_store end it "should set the flags to OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK" do @store.expects(:flags=).with OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK @host.ssl_store end end end describe "when waiting for a cert" do before do @host = Puppet::SSL::Host.new("me") end it "should generate its certificate request and attempt to read the certificate again if no certificate is found" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate) @host.wait_for_cert(1) end it "should catch and log errors during CSR saving" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate).raises(RuntimeError).then.returns nil @host.stubs(:sleep) @host.wait_for_cert(1) end it "should sleep and retry after failures saving the CSR if waitforcert is enabled" do @host.expects(:certificate).times(2).returns(nil).then.returns "foo" @host.expects(:generate).raises(RuntimeError).then.returns nil @host.expects(:sleep).with(1) @host.wait_for_cert(1) end it "should exit after failures saving the CSR of waitforcert is disabled" do @host.expects(:certificate).returns(nil) @host.expects(:generate).raises(RuntimeError) @host.expects(:puts) expect { @host.wait_for_cert(0) }.to exit_with 1 end it "should exit if the wait time is 0 and it can neither find nor retrieve a certificate" do @host.stubs(:certificate).returns nil @host.expects(:generate) @host.expects(:puts) expect { @host.wait_for_cert(0) }.to exit_with 1 end it "should sleep for the specified amount of time if no certificate is found after generating its certificate request" do @host.expects(:certificate).times(3).returns(nil).then.returns(nil).then.returns "foo" @host.expects(:generate) @host.expects(:sleep).with(1) @host.wait_for_cert(1) end it "should catch and log exceptions during certificate retrieval" do @host.expects(:certificate).times(3).returns(nil).then.raises(RuntimeError).then.returns("foo") @host.stubs(:generate) @host.stubs(:sleep) Puppet.expects(:err) @host.wait_for_cert(1) end end describe "when handling PSON" do include PuppetSpec::Files before do Puppet[:vardir] = tmpdir("ssl_test_vardir") Puppet[:ssldir] = tmpdir("ssl_test_ssldir") Puppet::SSLCertificates::CA.new.mkrootcert # localcacert is where each client stores the CA certificate # cacert is where the master stores the CA certificate # Since we need to play the role of both for testing we need them to be the same and exist Puppet[:cacert] = Puppet[:localcacert] @ca=Puppet::SSL::CertificateAuthority.new end describe "when converting to PSON" do it "should be able to identify a host with an unsigned certificate request" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request pson_hash = { "fingerprint" => host.certificate_request.fingerprint, "desired_state" => 'requested', "name" => host.name } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end it "should be able to identify a host with a signed certificate" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request @ca.sign(host.name) pson_hash = { "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint, "desired_state" => 'signed', "name" => host.name, } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end it "should be able to identify a host with a revoked certificate" do host = Puppet::SSL::Host.new("bazinga") host.generate_certificate_request @ca.sign(host.name) @ca.revoke(host.name) pson_hash = { "fingerprint" => Puppet::SSL::Certificate.indirection.find(host.name).fingerprint, "desired_state" => 'revoked', "name" => host.name, } result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson) result["fingerprint"].should == pson_hash["fingerprint"] result["name"].should == pson_hash["name"] result["state"].should == pson_hash["desired_state"] end end describe "when converting from PSON" do it "should return a Puppet::SSL::Host object with the specified desired state" do host = Puppet::SSL::Host.new("bazinga") host.desired_state="signed" pson_hash = { "name" => host.name, "desired_state" => host.desired_state, } generated_host = Puppet::SSL::Host.from_pson(pson_hash) generated_host.desired_state.should == host.desired_state generated_host.name.should == host.name end end end end diff --git a/spec/unit/ssl/inventory_spec.rb b/spec/unit/ssl/inventory_spec.rb index d8606b1b4..3d141d0cd 100755 --- a/spec/unit/ssl/inventory_spec.rb +++ b/spec/unit/ssl/inventory_spec.rb @@ -1,179 +1,179 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/ssl/inventory' describe Puppet::SSL::Inventory do before do @class = Puppet::SSL::Inventory end it "should use the :certinventory setting for the path to the inventory file" do Puppet.settings.expects(:value).with(:cert_inventory).returns "/inven/tory" @class.any_instance.stubs(:rebuild) @class.new.path.should == "/inven/tory" end describe "when initializing" do it "should set its path to the inventory file" do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" @class.new.path.should == "/inven/tory" end end describe "when managing an inventory" do before do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" FileTest.stubs(:exist?).with("/inven/tory").returns true @inventory = @class.new @cert = mock 'cert' end describe "and creating the inventory file" do before do Puppet.settings.stubs(:write) FileTest.stubs(:exist?).with("/inven/tory").returns false Puppet::SSL::Certificate.indirection.stubs(:search).returns [] end it "should log that it is building a new inventory file" do Puppet.expects(:notice) @inventory.rebuild end it "should use the Settings to write to the file" do Puppet.settings.expects(:write).with(:cert_inventory) @inventory.rebuild end it "should add a header to the file" do fh = mock 'filehandle' Puppet.settings.stubs(:write).yields fh fh.expects(:print).with { |str| str =~ /^#/ } @inventory.rebuild end it "should add formatted information on all existing certificates" do cert1 = mock 'cert1' cert2 = mock 'cert2' Puppet::SSL::Certificate.indirection.expects(:search).with("*").returns [cert1, cert2] @class.any_instance.expects(:add).with(cert1) @class.any_instance.expects(:add).with(cert2) @inventory.rebuild end end describe "and adding a certificate" do it "should build the inventory file if one does not exist" do Puppet.settings.stubs(:value).with(:cert_inventory).returns "/inven/tory" Puppet.settings.stubs(:write) FileTest.expects(:exist?).with("/inven/tory").returns false @inventory.expects(:rebuild) @inventory.add(@cert) end it "should use the Settings to write to the file" do Puppet.settings.expects(:write).with(:cert_inventory, "a") @inventory.add(@cert) end it "should use the actual certificate if it was passed a Puppet certificate" do cert = Puppet::SSL::Certificate.new("mycert") cert.content = @cert fh = stub 'filehandle', :print => nil Puppet.settings.stubs(:write).yields fh @inventory.expects(:format).with(@cert) @inventory.add(@cert) end it "should add formatted certificate information to the end of the file" do fh = mock 'filehandle' Puppet.settings.stubs(:write).yields fh @inventory.expects(:format).with(@cert).returns "myformat" fh.expects(:print).with("myformat") @inventory.add(@cert) end end - describe "and formatting a certificate" do + describe "and formatting a certificate", :fails_on_windows => true do before do @cert = stub 'cert', :not_before => Time.now, :not_after => Time.now, :subject => "mycert", :serial => 15 end it "should print the serial number as a 4 digit hex number in the first field" do @inventory.format(@cert).split[0].should == "0x000f" # 15 in hex end it "should print the not_before date in '%Y-%m-%dT%H:%M:%S%Z' format in the second field" do @cert.not_before.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "before_time" @inventory.format(@cert).split[1].should == "before_time" end it "should print the not_after date in '%Y-%m-%dT%H:%M:%S%Z' format in the third field" do @cert.not_after.expects(:strftime).with('%Y-%m-%dT%H:%M:%S%Z').returns "after_time" @inventory.format(@cert).split[2].should == "after_time" end it "should print the subject in the fourth field" do @inventory.format(@cert).split[3].should == "mycert" end it "should add a carriage return" do @inventory.format(@cert).should =~ /\n$/ end it "should produce a line consisting of the serial number, start date, expiration date, and subject" do # Just make sure our serial and subject bracket the lines. @inventory.format(@cert).should =~ /^0x.+mycert$/ end end it "should be able to find a given host's serial number" do @inventory.should respond_to(:serial) end describe "and finding a serial number" do it "should return nil if the inventory file is missing" do FileTest.expects(:exist?).with("/inven/tory").returns false @inventory.serial(:whatever).should be_nil end it "should return the serial number from the line matching the provided name" do File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] @inventory.serial("me").should == 15 end it "should return the number as an integer" do File.expects(:readlines).with("/inven/tory").returns ["0x00f blah blah /CN=me\n", "0x001 blah blah /CN=you\n"] @inventory.serial("me").should == 15 end end end end diff --git a/spec/unit/sslcertificates/ca_spec.rb b/spec/unit/sslcertificates/ca_spec.rb index eea246ba1..2ff4036dd 100755 --- a/spec/unit/sslcertificates/ca_spec.rb +++ b/spec/unit/sslcertificates/ca_spec.rb @@ -1,110 +1,110 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet' require 'puppet/sslcertificates' require 'puppet/sslcertificates/ca' -describe Puppet::SSLCertificates::CA do +describe Puppet::SSLCertificates::CA, :fails_on_windows => true do before :all do @hosts = %w{host.domain.com Other.Testing.Com} end before :each do Puppet::Util::SUIDManager.stubs(:asuser).yields file = Tempfile.new("ca_testing") @dir = file.path file.delete Puppet.settings[:confdir] = @dir Puppet.settings[:vardir] = @dir @ca = Puppet::SSLCertificates::CA.new end after :each do system("rm -rf #{@dir}") end describe 'when cleaning' do it 'should remove associated files' do dirs = [:csrdir, :signeddir, :publickeydir, :privatekeydir, :certdir] @hosts.each do |host| files = [] dirs.each do |dir| dir = Puppet[dir] # Case insensitivity is handled through downcasing file = File.join(dir, host.downcase + '.pem') File.open(file, "w") do |f| f.puts "testing" end files << file end lambda { @ca.clean(host) }.should_not raise_error files.reject {|f| ! File.exists?(f)}.should be_empty end end end describe 'when mapping hosts to files' do it 'should correctly return the certfile' do @hosts.each do |host| value = nil lambda { value = @ca.host2certfile host }.should_not raise_error File.join(Puppet[:signeddir], host.downcase + '.pem').should == value end end it 'should correctly return the csrfile' do @hosts.each do |host| value = nil lambda { value = @ca.host2csrfile host }.should_not raise_error File.join(Puppet[:csrdir], host.downcase + '.pem').should == value end end end describe 'when listing' do it 'should find all csr' do list = [] # Make some fake CSRs @hosts.each do |host| file = File.join(Puppet[:csrdir], host.downcase + '.pem') File.open(file, 'w') { |f| f.puts "yay" } list << host.downcase end @ca.list.sort.should == list.sort end end describe 'when creating a root certificate' do before :each do lambda { @ca.mkrootcert }.should_not raise_exception end it 'should store the public key' do File.exists?(Puppet[:capub]).should be_true end it 'should prepend "Puppet CA: " to the fqdn as the ca_name by default' do host_mock_fact = mock() host_mock_fact.expects(:value).returns('myhost') domain_mock_fact = mock() domain_mock_fact.expects(:value).returns('puppetlabs.lan') Facter.stubs(:[]).with('hostname').returns(host_mock_fact) Facter.stubs(:[]).with('domain').returns(domain_mock_fact) @ca.mkrootcert.name.should == 'Puppet CA: myhost.puppetlabs.lan' end end end diff --git a/spec/unit/transaction/event_manager_spec.rb b/spec/unit/transaction/event_manager_spec.rb index d127d0391..37775997d 100755 --- a/spec/unit/transaction/event_manager_spec.rb +++ b/spec/unit/transaction/event_manager_spec.rb @@ -1,259 +1,261 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/transaction/event_manager' describe Puppet::Transaction::EventManager do + include PuppetSpec::Files + describe "at initialization" do it "should require a transaction" do Puppet::Transaction::EventManager.new("trans").transaction.should == "trans" end end it "should delegate its relationship graph to the transaction" do transaction = stub 'transaction' manager = Puppet::Transaction::EventManager.new(transaction) transaction.expects(:relationship_graph).returns "mygraph" manager.relationship_graph.should == "mygraph" end describe "when queueing events" do before do @manager = Puppet::Transaction::EventManager.new(@transaction) - @resource = Puppet::Type.type(:file).new :path => "/my/file" + @resource = Puppet::Type.type(:file).new :path => make_absolute("/my/file") @graph = stub 'graph', :matching_edges => [], :resource => @resource @manager.stubs(:relationship_graph).returns @graph @event = Puppet::Transaction::Event.new(:name => :foo, :resource => @resource) end it "should store all of the events in its event list" do @event2 = Puppet::Transaction::Event.new(:name => :bar, :resource => @resource) @manager.queue_events(@resource, [@event, @event2]) @manager.events.should include(@event) @manager.events.should include(@event2) end it "should queue events for the target and callback of any matching edges" do edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1", :c1 => nil)) edge2 = stub("edge2", :callback => :c2, :source => stub("s2"), :target => stub("t2", :c2 => nil)) @graph.expects(:matching_edges).with { |event, resource| event == @event }.returns [edge1, edge2] @manager.expects(:queue_events_for_resource).with(@resource, edge1.target, edge1.callback, [@event]) @manager.expects(:queue_events_for_resource).with(@resource, edge2.target, edge2.callback, [@event]) @manager.queue_events(@resource, [@event]) end it "should queue events for the changed resource if the resource is self-refreshing and not being deleted" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns true @resource.expects(:deleting?).returns false @manager.expects(:queue_events_for_resource).with(@resource, @resource, :refresh, [@event]) @manager.queue_events(@resource, [@event]) end it "should not queue events for the changed resource if the resource is not self-refreshing" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns false @resource.stubs(:deleting?).returns false @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should not queue events for the changed resource if the resource is being deleted" do @graph.stubs(:matching_edges).returns [] @resource.expects(:self_refresh?).returns true @resource.expects(:deleting?).returns true @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should ignore edges that don't have a callback" do edge1 = stub("edge1", :callback => :nil, :source => stub("s1"), :target => stub("t1", :c1 => nil)) @graph.expects(:matching_edges).returns [edge1] @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end it "should ignore targets that don't respond to the callback" do edge1 = stub("edge1", :callback => :c1, :source => stub("s1"), :target => stub("t1")) @graph.expects(:matching_edges).returns [edge1] @manager.expects(:queue_events_for_resource).never @manager.queue_events(@resource, [@event]) end end describe "when queueing events for a resource" do before do @transaction = stub 'transaction' @manager = Puppet::Transaction::EventManager.new(@transaction) end it "should do nothing if no events are queued" do @manager.queued_events(stub("target")) { |callback, events| raise "should never reach this" } end it "should yield the callback and events for each callback" do target = stub("target") 2.times do |i| @manager.queue_events_for_resource(stub("source", :info => nil), target, "callback#{i}", ["event#{i}"]) end @manager.queued_events(target) { |callback, events| } end it "should use the source to log that it's scheduling a refresh of the target" do target = stub("target") source = stub 'source' source.expects(:info) @manager.queue_events_for_resource(source, target, "callback", ["event"]) @manager.queued_events(target) { |callback, events| } end end describe "when processing events for a given resource" do before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @manager = Puppet::Transaction::EventManager.new(@transaction) @manager.stubs(:queue_events) - @resource = Puppet::Type.type(:file).new :path => "/my/file" + @resource = Puppet::Type.type(:file).new :path => make_absolute("/my/file") @event = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) end it "should call the required callback once for each set of associated events" do @manager.expects(:queued_events).with(@resource).multiple_yields([:callback1, [@event]], [:callback2, [@event]]) @resource.expects(:callback1) @resource.expects(:callback2) @manager.process_events(@resource) end it "should set the 'restarted' state on the resource status" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @manager.process_events(@resource) @transaction.resource_status(@resource).should be_restarted end it "should queue a 'restarted' event generated by the resource" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @resource.expects(:event).with(:name => :restarted, :status => "success").returns "myevent" @manager.expects(:queue_events).with(@resource, ["myevent"]) @manager.process_events(@resource) end it "should log that it restarted" do @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) @resource.stubs(:callback1) @resource.expects(:notice).with { |msg| msg.include?("Triggered 'callback1'") } @manager.process_events(@resource) end describe "and the events include a noop event and at least one non-noop event" do before do @event.stubs(:status).returns "noop" @event2 = Puppet::Transaction::Event.new(:name => :event, :resource => @resource) @event2.status = "success" @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event, @event2]) end it "should call the callback" do @resource.expects(:callback1) @manager.process_events(@resource) end end describe "and the events are all noop events" do before do @event.stubs(:status).returns "noop" @resource.stubs(:event).returns(Puppet::Transaction::Event.new) @manager.expects(:queued_events).with(@resource).yields(:callback1, [@event]) end it "should log" do @resource.expects(:notice).with { |msg| msg.include?("Would have triggered 'callback1'") } @manager.process_events(@resource) end it "should not call the callback" do @resource.expects(:callback1).never @manager.process_events(@resource) end it "should queue a new noop event generated from the resource" do event = Puppet::Transaction::Event.new @resource.expects(:event).with(:status => "noop", :name => :noop_restart).returns event @manager.expects(:queue_events).with(@resource, [event]) @manager.process_events(@resource) end end describe "and the callback fails" do before do @resource.expects(:callback1).raises "a failure" @resource.stubs(:err) @manager.expects(:queued_events).yields(:callback1, [@event]) end it "should log but not fail" do @resource.expects(:err) lambda { @manager.process_events(@resource) }.should_not raise_error end it "should set the 'failed_restarts' state on the resource status" do @manager.process_events(@resource) @transaction.resource_status(@resource).should be_failed_to_restart end it "should not queue a 'restarted' event" do @manager.expects(:queue_events).never @manager.process_events(@resource) end it "should set the 'restarted' state on the resource status" do @manager.process_events(@resource) @transaction.resource_status(@resource).should_not be_restarted end end end end diff --git a/spec/unit/transaction/event_spec.rb b/spec/unit/transaction/event_spec.rb index 0093baeb9..5f7f367b4 100755 --- a/spec/unit/transaction/event_spec.rb +++ b/spec/unit/transaction/event_spec.rb @@ -1,126 +1,128 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/transaction/event' describe Puppet::Transaction::Event do + include PuppetSpec::Files + [:previous_value, :desired_value, :property, :resource, :name, :message, :file, :line, :tags, :audited].each do |attr| it "should support #{attr}", :'fails_on_ruby_1.9.2' => true do event = Puppet::Transaction::Event.new event.send(attr.to_s + "=", "foo") event.send(attr).should == "foo" end end it "should always convert the property to a string" do Puppet::Transaction::Event.new(:property => :foo).property.should == "foo" end it "should always convert the resource to a string", :'fails_on_ruby_1.9.2' => true do Puppet::Transaction::Event.new(:resource => :foo).resource.should == "foo" end it "should produce the message when converted to a string" do event = Puppet::Transaction::Event.new event.expects(:message).returns "my message" event.to_s.should == "my message" end it "should support 'status'" do event = Puppet::Transaction::Event.new event.status = "success" event.status.should == "success" end it "should fail if the status is not to 'audit', 'noop', 'success', or 'failure" do event = Puppet::Transaction::Event.new lambda { event.status = "foo" }.should raise_error(ArgumentError) end it "should support tags" do Puppet::Transaction::Event.ancestors.should include(Puppet::Util::Tagging) end it "should create a timestamp at its creation time" do Puppet::Transaction::Event.new.time.should be_instance_of(Time) end describe "audit property" do it "should default to false" do Puppet::Transaction::Event.new.audited.should == false end end describe "when sending logs" do before do Puppet::Util::Log.stubs(:new) end it "should set the level to the resources's log level if the event status is 'success' and a resource is available" do resource = stub 'resource' resource.expects(:[]).with(:loglevel).returns :myloglevel Puppet::Util::Log.expects(:create).with { |args| args[:level] == :myloglevel } Puppet::Transaction::Event.new(:status => "success", :resource => resource).send_log end it "should set the level to 'notice' if the event status is 'success' and no resource is available" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } Puppet::Transaction::Event.new(:status => "success").send_log end it "should set the level to 'notice' if the event status is 'noop'" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :notice } Puppet::Transaction::Event.new(:status => "noop").send_log end it "should set the level to 'err' if the event status is 'failure'" do Puppet::Util::Log.expects(:new).with { |args| args[:level] == :err } Puppet::Transaction::Event.new(:status => "failure").send_log end it "should set the 'message' to the event log" do Puppet::Util::Log.expects(:new).with { |args| args[:message] == "my message" } Puppet::Transaction::Event.new(:message => "my message").send_log end it "should set the tags to the event tags" do Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} } Puppet::Transaction::Event.new(:tags => %w{one two}).send_log end [:file, :line].each do |attr| it "should pass the #{attr}" do Puppet::Util::Log.expects(:new).with { |args| args[attr] == "my val" } Puppet::Transaction::Event.new(attr => "my val").send_log end end it "should use the source description as the source if one is set", :'fails_on_ruby_1.9.2' => true do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "/my/param" } Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => "Foo[bar]", :property => "foo").send_log end it "should use the property as the source if one is available and no source description is set", :'fails_on_ruby_1.9.2' => true do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "foo" } Puppet::Transaction::Event.new(:resource => "Foo[bar]", :property => "foo").send_log end it "should use the property as the source if one is available and no property or source description is set", :'fails_on_ruby_1.9.2' => true do Puppet::Util::Log.expects(:new).with { |args| args[:source] == "Foo[bar]" } Puppet::Transaction::Event.new(:resource => "Foo[bar]").send_log end end describe "When converting to YAML" do it "should include only documented attributes" do - resource = Puppet::Type.type(:file).new(:title => "/tmp/foo") + resource = Puppet::Type.type(:file).new(:title => make_absolute("/tmp/foo")) event = Puppet::Transaction::Event.new(:source_description => "/my/param", :resource => resource, :file => "/foo.rb", :line => 27, :tags => %w{one two}, :desired_value => 7, :historical_value => 'Brazil', :message => "Help I'm trapped in a spec test", :name => :mode_changed, :previous_value => 6, :property => :mode, :status => 'success') event.to_yaml_properties.should == Puppet::Transaction::Event::YAML_ATTRIBUTES.sort end end end diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb index 4b04cc157..033c4c740 100755 --- a/spec/unit/transaction/report_spec.rb +++ b/spec/unit/transaction/report_spec.rb @@ -1,312 +1,312 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/transaction/report' describe Puppet::Transaction::Report do include PuppetSpec::Files before do Puppet::Util::Storage.stubs(:store) end it "should set its host name to the node_name_value" do Puppet[:node_name_value] = 'mynode' Puppet::Transaction::Report.new("apply").host.should == "mynode" end it "should return its host name as its name" do r = Puppet::Transaction::Report.new("apply") r.name.should == r.host end it "should create an initialization timestamp" do Time.expects(:now).returns "mytime" Puppet::Transaction::Report.new("apply").time.should == "mytime" end it "should take a 'kind' as an argument" do Puppet::Transaction::Report.new("inspect").kind.should == "inspect" end it "should take a 'configuration_version' as an argument" do Puppet::Transaction::Report.new("inspect", "some configuration version").configuration_version.should == "some configuration version" end it "should be able to set configuration_version" do report = Puppet::Transaction::Report.new("inspect") report.configuration_version = "some version" report.configuration_version.should == "some version" end it "should not include whits" do Puppet::FileBucket::File.indirection.stubs(:save) filename = tmpfile('whit_test') file = Puppet::Type.type(:file).new(:path => filename) catalog = Puppet::Resource::Catalog.new catalog.add_resource(file) report = Puppet::Transaction::Report.new("apply") catalog.apply(:report => report) report.finalize_report report.resource_statuses.values.any? {|res| res.resource_type =~ /whit/i}.should be_false report.metrics['time'].values.any? {|metric| metric.first =~ /whit/i}.should be_false end describe "when accepting logs" do before do @report = Puppet::Transaction::Report.new("apply") end it "should add new logs to the log list" do @report << "log" @report.logs[-1].should == "log" end it "should return self" do r = @report << "log" r.should equal(@report) end end describe "when accepting resource statuses" do before do @report = Puppet::Transaction::Report.new("apply") end it "should add each status to its status list" do status = stub 'status', :resource => "foo" @report.add_resource_status status @report.resource_statuses["foo"].should equal(status) end end describe "when using the indirector" do it "should redirect :save to the indirection" do Facter.stubs(:value).returns("eh") @indirection = stub 'indirection', :name => :report Puppet::Transaction::Report.stubs(:indirection).returns(@indirection) report = Puppet::Transaction::Report.new("apply") @indirection.expects(:save) Puppet::Transaction::Report.indirection.save(report) end it "should default to the 'processor' terminus" do Puppet::Transaction::Report.indirection.terminus_class.should == :processor end it "should delegate its name attribute to its host method" do report = Puppet::Transaction::Report.new("apply") report.expects(:host).returns "me" report.name.should == "me" end after do Puppet::Util::Cacher.expire end end describe "when computing exit status" do it "should produce 2 if changes are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 1}) report.add_metric("resources", {"failed" => 0}) report.exit_status.should == 2 end it "should produce 4 if failures are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 0}) report.add_metric("resources", {"failed" => 1}) report.exit_status.should == 4 end it "should produce 6 if both changes and failures are present" do report = Puppet::Transaction::Report.new("apply") report.add_metric("changes", {"total" => 1}) report.add_metric("resources", {"failed" => 1}) report.exit_status.should == 6 end end describe "before finalizing the report" do it "should have a status of 'failed'" do report = Puppet::Transaction::Report.new("apply") report.status.should == 'failed' end end describe "when finalizing the report" do before do @report = Puppet::Transaction::Report.new("apply") end def metric(name, value) if metric = @report.metrics[name.to_s] metric[value] else nil end end def add_statuses(count, type = :file) count.times do |i| - status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => "/my/path#{i}")) + status = Puppet::Resource::Status.new(Puppet::Type.type(type).new(:title => make_absolute("/my/path#{i}"))) yield status if block_given? @report.add_resource_status status end end [:time, :resources, :changes, :events].each do |type| it "should add #{type} metrics" do @report.finalize_report @report.metrics[type.to_s].should be_instance_of(Puppet::Transaction::Metric) end end describe "for resources" do it "should provide the total number of resources" do add_statuses(3) @report.finalize_report metric(:resources, "total").should == 3 end Puppet::Resource::Status::STATES.each do |state| it "should provide the number of #{state} resources as determined by the status objects" do add_statuses(3) { |status| status.send(state.to_s + "=", true) } @report.finalize_report metric(:resources, state.to_s).should == 3 end end it "should mark the report as 'failed' if there are failing resources" do add_statuses(1) { |status| status.failed = true } @report.finalize_report @report.status.should == 'failed' end end describe "for changes" do it "should provide the number of changes from the resource statuses and mark the report as 'changed'" do add_statuses(3) { |status| 3.times { status << Puppet::Transaction::Event.new(:status => 'success') } } @report.finalize_report metric(:changes, "total").should == 9 @report.status.should == 'changed' end it "should provide a total even if there are no changes, and mark the report as 'unchanged'" do @report.finalize_report metric(:changes, "total").should == 0 @report.status.should == 'unchanged' end end describe "for times" do - it "should provide the total amount of time for each resource type" do + it "should provide the total amount of time for each resource type", :fails_on_windows => true do add_statuses(3, :file) do |status| status.evaluation_time = 1 end add_statuses(3, :exec) do |status| status.evaluation_time = 2 end add_statuses(3, :mount) do |status| status.evaluation_time = 3 end @report.finalize_report metric(:time, "file").should == 3 metric(:time, "exec").should == 6 metric(:time, "mount").should == 9 end it "should add any provided times from external sources" do @report.add_times :foobar, 50 @report.finalize_report metric(:time, "foobar").should == 50 end it "should have a total time" do add_statuses(3, :file) do |status| status.evaluation_time = 1.25 end @report.add_times :config_retrieval, 0.5 @report.finalize_report metric(:time, "total").should == 4.25 end end describe "for events" do it "should provide the total number of events" do add_statuses(3) do |status| 3.times { |i| status.add_event(Puppet::Transaction::Event.new :status => 'success') } end @report.finalize_report metric(:events, "total").should == 9 end it "should provide the total even if there are no events" do @report.finalize_report metric(:events, "total").should == 0 end Puppet::Transaction::Event::EVENT_STATUSES.each do |status_name| it "should provide the number of #{status_name} events" do add_statuses(3) do |status| 3.times do |i| event = Puppet::Transaction::Event.new event.status = status_name status.add_event(event) end end @report.finalize_report metric(:events, status_name).should == 9 end end end end describe "when producing a summary" do before do resource = Puppet::Type.type(:notify).new(:name => "testing") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource trans = catalog.apply @report = trans.report @report.finalize_report end %w{changes time resources events}.each do |main| it "should include the key #{main} in the raw summary hash" do @report.raw_summary.should be_key main end end it "should include the last run time in the raw summary hash" do Time.stubs(:now).returns(Time.utc(2010,11,10,12,0,24)) @report.raw_summary["time"]["last_run"].should == 1289390424 end %w{Changes Total Resources Time Events}.each do |main| it "should include information on #{main} in the textual summary" do @report.summary.should be_include(main) end end end describe "when outputting yaml" do it "should not include @external_times" do report = Puppet::Transaction::Report.new('apply') report.add_times('config_retrieval', 1.0) report.to_yaml_properties.should_not include('@external_times') end end end diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb index a594d3669..cadc31a0f 100755 --- a/spec/unit/transaction/resource_harness_spec.rb +++ b/spec/unit/transaction/resource_harness_spec.rb @@ -1,484 +1,484 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/transaction/resource_harness' describe Puppet::Transaction::ResourceHarness do include PuppetSpec::Files before do @mode_750 = Puppet.features.microsoft_windows? ? '644' : '750' @mode_755 = Puppet.features.microsoft_windows? ? '644' : '755' - path = Puppet.features.microsoft_windows? ? "c:/my/file" : "/my/file" + path = make_absolute("/my/file") @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @resource = Puppet::Type.type(:file).new :path => path @harness = Puppet::Transaction::ResourceHarness.new(@transaction) @current_state = Puppet::Resource.new(:file, path) @resource.stubs(:retrieve).returns @current_state @status = Puppet::Resource::Status.new(@resource) Puppet::Resource::Status.stubs(:new).returns @status end it "should accept a transaction at initialization" do harness = Puppet::Transaction::ResourceHarness.new(@transaction) harness.transaction.should equal(@transaction) end it "should delegate to the transaction for its relationship graph" do @transaction.expects(:relationship_graph).returns "relgraph" Puppet::Transaction::ResourceHarness.new(@transaction).relationship_graph.should == "relgraph" end describe "when evaluating a resource" do it "should create and return a resource status instance for the resource" do @harness.evaluate(@resource).should be_instance_of(Puppet::Resource::Status) end it "should fail if no status can be created" do Puppet::Resource::Status.expects(:new).raises ArgumentError lambda { @harness.evaluate(@resource) }.should raise_error end it "should retrieve the current state of the resource" do @resource.expects(:retrieve).returns @current_state @harness.evaluate(@resource) end it "should mark the resource as failed and return if the current state cannot be retrieved" do @resource.expects(:retrieve).raises ArgumentError @harness.evaluate(@resource).should be_failed end it "should store the resource's evaluation time in the resource status" do @harness.evaluate(@resource).evaluation_time.should be_instance_of(Float) end end def events_to_hash(events) events.map do |event| hash = {} event.instance_variables.each do |varname| hash[varname] = event.instance_variable_get(varname.to_sym) end hash end end def make_stub_provider stubProvider = Class.new(Puppet::Type) stubProvider.instance_eval do initvars newparam(:name) do desc "The name var" isnamevar end newproperty(:foo) do desc "A property that can be changed successfully" def sync end def retrieve :absent end def insync?(reference_value) false end end newproperty(:bar) do desc "A property that raises an exception when you try to change it" def sync raise ZeroDivisionError.new('bar') end def retrieve :absent end def insync?(reference_value) false end end end stubProvider end describe "when an error occurs" do before :each do stub_provider = make_stub_provider resource = stub_provider.new :name => 'name', :foo => 1, :bar => 2 resource.expects(:err).never @status = @harness.evaluate(resource) end it "should record previous successful events" do @status.events[0].property.should == 'foo' @status.events[0].status.should == 'success' end it "should record a failure event" do @status.events[1].property.should == 'bar' @status.events[1].status.should == 'failure' end end describe "when auditing" do it "should not call insync? on parameters that are merely audited" do stub_provider = make_stub_provider resource = stub_provider.new :name => 'name', :audit => ['foo'] resource.property(:foo).expects(:insync?).never status = @harness.evaluate(resource) status.events.each do |event| event.status.should != 'failure' end end it "should be able to audit a file's group" do # see bug #5710 test_file = tmpfile('foo') File.open(test_file, 'w').close resource = Puppet::Type.type(:file).new :path => test_file, :audit => ['group'], :backup => false resource.expects(:err).never # make sure no exceptions get swallowed status = @harness.evaluate(resource) status.events.each do |event| event.status.should != 'failure' end end end describe "when applying changes" do [false, true].each do |noop_mode|; describe (noop_mode ? "in noop mode" : "in normal mode") do [nil, @mode_750].each do |machine_state|; describe (machine_state ? "with a file initially present" : "with no file initially present") do [nil, @mode_750, @mode_755].each do |yaml_mode| [nil, :file, :absent].each do |yaml_ensure|; describe "with mode=#{yaml_mode.inspect} and ensure=#{yaml_ensure.inspect} stored in state.yml" do [false, true].each do |auditing_ensure| [false, true].each do |auditing_mode| auditing = [] auditing.push(:mode) if auditing_mode auditing.push(:ensure) if auditing_ensure [nil, :file, :absent].each do |ensure_property| # what we set "ensure" to in the manifest [nil, @mode_750, @mode_755].each do |mode_property| # what we set "mode" to in the manifest manifest_settings = {} manifest_settings[:audit] = auditing if !auditing.empty? manifest_settings[:ensure] = ensure_property if ensure_property manifest_settings[:mode] = mode_property if mode_property describe "with manifest settings #{manifest_settings.inspect}" do; it "should behave properly" do # Set up preconditions test_file = tmpfile('foo') if machine_state File.open(test_file, 'w', machine_state.to_i(8)).close end Puppet[:noop] = noop_mode params = { :path => test_file, :backup => false } params.merge!(manifest_settings) resource = Puppet::Type.type(:file).new params @harness.cache(resource, :mode, yaml_mode) if yaml_mode @harness.cache(resource, :ensure, yaml_ensure) if yaml_ensure fake_time = Time.utc(2011, 'jan', 3, 12, 24, 0) Time.stubs(:now).returns(fake_time) # So that Puppet::Resource::Status objects will compare properly resource.expects(:err).never # make sure no exceptions get swallowed status = @harness.evaluate(resource) # do the thing # check that the state of the machine has been properly updated expected_logs = [] expected_status_events = [] if auditing_mode @harness.cached(resource, :mode).should == (machine_state || :absent) else @harness.cached(resource, :mode).should == yaml_mode end if auditing_ensure @harness.cached(resource, :ensure).should == (machine_state ? :file : :absent) else @harness.cached(resource, :ensure).should == yaml_ensure end if ensure_property == :file file_would_be_there_if_not_noop = true elsif ensure_property == nil file_would_be_there_if_not_noop = machine_state != nil else # ensure_property == :absent file_would_be_there_if_not_noop = false end file_should_be_there = noop_mode ? machine_state != nil : file_would_be_there_if_not_noop File.exists?(test_file).should == file_should_be_there if file_should_be_there if noop_mode expected_file_mode = machine_state else expected_file_mode = mode_property || machine_state end if !expected_file_mode # we didn't specify a mode and the file was created, so mode comes from umode else file_mode = File.stat(test_file).mode & 0777 file_mode.should == expected_file_mode.to_i(8) end end # Test log output for the "mode" parameter previously_recorded_mode_already_logged = false mode_status_msg = nil if machine_state && file_would_be_there_if_not_noop && mode_property && machine_state != mode_property if noop_mode what_happened = "current_value #{machine_state}, should be #{mode_property} (noop)" expected_status = 'noop' else what_happened = "mode changed '#{machine_state}' to '#{mode_property}'" expected_status = 'success' end if auditing_mode && yaml_mode && yaml_mode != machine_state previously_recorded_mode_already_logged = true mode_status_msg = "#{what_happened} (previously recorded value was #{yaml_mode})" else mode_status_msg = what_happened end expected_logs << "notice: /#{resource}/mode: #{mode_status_msg}" end if @harness.cached(resource, :mode) && @harness.cached(resource, :mode) != yaml_mode if yaml_mode unless previously_recorded_mode_already_logged mode_status_msg = "audit change: previously recorded value #{yaml_mode} has been changed to #{@harness.cached(resource, :mode)}" expected_logs << "notice: /#{resource}/mode: #{mode_status_msg}" expected_status = 'audit' end else expected_logs << "notice: /#{resource}/mode: audit change: newly-recorded value #{@harness.cached(resource, :mode)}" end end if mode_status_msg expected_status_events << Puppet::Transaction::Event.new( :source_description => "/#{resource}/mode", :resource => resource, :file => nil, :line => nil, :tags => %w{file}, :desired_value => mode_property, :historical_value => yaml_mode, :message => mode_status_msg, :name => :mode_changed, :previous_value => machine_state || :absent, :property => :mode, :status => expected_status, :audited => auditing_mode) end # Test log output for the "ensure" parameter previously_recorded_ensure_already_logged = false ensure_status_msg = nil if file_would_be_there_if_not_noop != (machine_state != nil) if noop_mode what_happened = "current_value #{machine_state ? 'file' : 'absent'}, should be #{file_would_be_there_if_not_noop ? 'file' : 'absent'} (noop)" expected_status = 'noop' else what_happened = file_would_be_there_if_not_noop ? 'created' : 'removed' expected_status = 'success' end if auditing_ensure && yaml_ensure && yaml_ensure != (machine_state ? :file : :absent) previously_recorded_ensure_already_logged = true ensure_status_msg = "#{what_happened} (previously recorded value was #{yaml_ensure})" else ensure_status_msg = "#{what_happened}" end expected_logs << "notice: /#{resource}/ensure: #{ensure_status_msg}" end if @harness.cached(resource, :ensure) && @harness.cached(resource, :ensure) != yaml_ensure if yaml_ensure unless previously_recorded_ensure_already_logged ensure_status_msg = "audit change: previously recorded value #{yaml_ensure} has been changed to #{@harness.cached(resource, :ensure)}" expected_logs << "notice: /#{resource}/ensure: #{ensure_status_msg}" expected_status = 'audit' end else expected_logs << "notice: /#{resource}/ensure: audit change: newly-recorded value #{@harness.cached(resource, :ensure)}" end end if ensure_status_msg if ensure_property == :file ensure_event_name = :file_created elsif ensure_property == nil ensure_event_name = :file_changed else # ensure_property == :absent ensure_event_name = :file_removed end expected_status_events << Puppet::Transaction::Event.new( :source_description => "/#{resource}/ensure", :resource => resource, :file => nil, :line => nil, :tags => %w{file}, :desired_value => ensure_property, :historical_value => yaml_ensure, :message => ensure_status_msg, :name => ensure_event_name, :previous_value => machine_state ? :file : :absent, :property => :ensure, :status => expected_status, :audited => auditing_ensure) end # Actually check the logs. @logs.map {|l| "#{l.level}: #{l.source}: #{l.message}"}.should =~ expected_logs # All the log messages should show up as events except the "newly-recorded" ones. expected_event_logs = @logs.reject {|l| l.message =~ /newly-recorded/ } status.events.map {|e| e.message}.should =~ expected_event_logs.map {|l| l.message } events_to_hash(status.events).should =~ events_to_hash(expected_status_events) # Check change count - this is the number of changes that actually occurred. expected_change_count = 0 if (machine_state != nil) != file_should_be_there expected_change_count = 1 elsif machine_state != nil if expected_file_mode != machine_state expected_change_count = 1 end end status.change_count.should == expected_change_count # Check out of sync count - this is the number # of changes that would have occurred in # non-noop mode. expected_out_of_sync_count = 0 if (machine_state != nil) != file_would_be_there_if_not_noop expected_out_of_sync_count = 1 elsif machine_state != nil if mode_property != nil && mode_property != machine_state expected_out_of_sync_count = 1 end end if !noop_mode expected_out_of_sync_count.should == expected_change_count end status.out_of_sync_count.should == expected_out_of_sync_count # Check legacy summary fields status.changed.should == (expected_change_count != 0) status.out_of_sync.should == (expected_out_of_sync_count != 0) # Check the :synced field on state.yml synced_should_be_set = !noop_mode && status.changed (@harness.cached(resource, :synced) != nil).should == synced_should_be_set end; end end end end end end; end end end; end end; end it "should not apply changes if allow_changes?() returns false" do test_file = tmpfile('foo') resource = Puppet::Type.type(:file).new :path => test_file, :backup => false, :ensure => :file resource.expects(:err).never # make sure no exceptions get swallowed @harness.expects(:allow_changes?).with(resource).returns false status = @harness.evaluate(resource) File.exists?(test_file).should == false end end describe "when determining whether the resource can be changed" do before do @resource.stubs(:purging?).returns true @resource.stubs(:deleting?).returns true end it "should be true if the resource is not being purged" do @resource.expects(:purging?).returns false @harness.should be_allow_changes(@resource) end it "should be true if the resource is not being deleted" do @resource.expects(:deleting?).returns false @harness.should be_allow_changes(@resource) end it "should be true if the resource has no dependents" do @harness.relationship_graph.expects(:dependents).with(@resource).returns [] @harness.should be_allow_changes(@resource) end it "should be true if all dependents are being deleted" do dep = stub 'dependent', :deleting? => true @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] @resource.expects(:purging?).returns true @harness.should be_allow_changes(@resource) end it "should be false if the resource's dependents are not being deleted" do dep = stub 'dependent', :deleting? => false, :ref => "myres" @resource.expects(:warning) @harness.relationship_graph.expects(:dependents).with(@resource).returns [dep] @harness.should_not be_allow_changes(@resource) end end describe "when finding the schedule" do before do @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog end it "should warn and return nil if the resource has no catalog" do @resource.catalog = nil @resource.expects(:warning) @harness.schedule(@resource).should be_nil end it "should return nil if the resource specifies no schedule" do @harness.schedule(@resource).should be_nil end it "should fail if the named schedule cannot be found" do @resource[:schedule] = "whatever" @resource.expects(:fail) @harness.schedule(@resource) end it "should return the named schedule if it exists" do sched = Puppet::Type.type(:schedule).new(:name => "sched") @catalog.add_resource(sched) @resource[:schedule] = "sched" @harness.schedule(@resource).to_s.should == sched.to_s end end describe "when determining if a resource is scheduled" do before do @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @status = Puppet::Resource::Status.new(@resource) end it "should return true if 'ignoreschedules' is set" do Puppet[:ignoreschedules] = true @resource[:schedule] = "meh" @harness.should be_scheduled(@status, @resource) end it "should return true if the resource has no schedule set" do @harness.should be_scheduled(@status, @resource) end it "should return the result of matching the schedule with the cached 'checked' time if a schedule is set" do t = Time.now @harness.expects(:cached).with(@resource, :checked).returns(t) sched = Puppet::Type.type(:schedule).new(:name => "sched") @catalog.add_resource(sched) @resource[:schedule] = "sched" sched.expects(:match?).with(t.to_i).returns "feh" @harness.scheduled?(@status, @resource).should == "feh" end end it "should be able to cache data in the Storage module" do data = {} Puppet::Util::Storage.expects(:cache).with(@resource).returns data @harness.cache(@resource, :foo, "something") data[:foo].should == "something" end it "should be able to retrieve data from the cache" do data = {:foo => "other"} Puppet::Util::Storage.expects(:cache).with(@resource).returns data @harness.cached(@resource, :foo).should == "other" end end diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb index 3829cfaf5..3f34f65bd 100755 --- a/spec/unit/transaction_spec.rb +++ b/spec/unit/transaction_spec.rb @@ -1,462 +1,464 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/transaction' def without_warnings flag = $VERBOSE $VERBOSE = nil yield $VERBOSE = flag end describe Puppet::Transaction do + include PuppetSpec::Files + before do - @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" + @basepath = make_absolute("/what/ever") @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) end it "should delegate its event list to the event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.expects(:events).returns %w{my events} @transaction.events.should == %w{my events} end it "should delegate adding times to its report" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.report.expects(:add_times).with(:foo, 10) @transaction.report.expects(:add_times).with(:bar, 20) @transaction.add_times :foo => 10, :bar => 20 end it "should be able to accept resource status instances" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource).should equal(status) end it "should be able to look resource status up by resource reference" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.resource_status(resource.to_s).should equal(status) end # This will basically only ever be used during testing. it "should automatically create resource statuses if asked for a non-existent status" do resource = Puppet::Type.type(:notify).new :title => "foobar" @transaction.resource_status(resource).should be_instance_of(Puppet::Resource::Status) end it "should add provided resource statuses to its report" do resource = Puppet::Type.type(:notify).new :title => "foobar" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.report.resource_statuses[resource.to_s].should equal(status) end it "should consider a resource to be failed if a status instance exists for that resource and indicates it is failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_failed(resource) end it "should not consider a resource to be failed if a status instance exists for that resource but indicates it is not failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_failed(resource) end it "should consider there to be failed resources if any statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) status.failed = "some message" @transaction.add_resource_status(status) @transaction.should be_any_failed end it "should not consider there to be failed resources if no statuses are marked failed" do resource = Puppet::Type.type(:notify).new :name => "yayness" status = Puppet::Resource::Status.new(resource) @transaction.add_resource_status(status) @transaction.should_not be_any_failed end it "should use the provided report object" do report = Puppet::Transaction::Report.new("apply") @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new, report) @transaction.report.should == report end it "should create a report if none is provided" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.report.should be_kind_of Puppet::Transaction::Report end describe "when initializing" do it "should create an event manager" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.should be_instance_of(Puppet::Transaction::EventManager) @transaction.event_manager.transaction.should equal(@transaction) end it "should create a resource harness" do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.resource_harness.should be_instance_of(Puppet::Transaction::ResourceHarness) @transaction.resource_harness.transaction.should equal(@transaction) end end describe "when evaluating a resource" do before do @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.stubs(:skip?).returns false @resource = Puppet::Type.type(:file).new :path => @basepath end it "should check whether the resource should be skipped" do @transaction.expects(:skip?).with(@resource).returns false @transaction.eval_resource(@resource) end it "should process events" do @transaction.event_manager.expects(:process_events).with(@resource) @transaction.eval_resource(@resource) end describe "and the resource should be skipped" do before do @transaction.expects(:skip?).with(@resource).returns true end it "should mark the resource's status as skipped" do @transaction.eval_resource(@resource) @transaction.resource_status(@resource).should be_skipped end end end describe "when applying a resource" do before do @resource = Puppet::Type.type(:file).new :path => @basepath @status = Puppet::Resource::Status.new(@resource) @transaction = Puppet::Transaction.new(Puppet::Resource::Catalog.new) @transaction.event_manager.stubs(:queue_events) @transaction.resource_harness.stubs(:evaluate).returns(@status) end it "should use its resource harness to apply the resource" do @transaction.resource_harness.expects(:evaluate).with(@resource) @transaction.apply(@resource) end it "should add the resulting resource status to its status list" do @transaction.apply(@resource) @transaction.resource_status(@resource).should be_instance_of(Puppet::Resource::Status) end it "should queue any events added to the resource status" do @status.expects(:events).returns %w{a b} @transaction.event_manager.expects(:queue_events).with(@resource, ["a", "b"]) @transaction.apply(@resource) end it "should log and skip any resources that cannot be applied" do @transaction.resource_harness.expects(:evaluate).raises ArgumentError @resource.expects(:err) @transaction.apply(@resource) @transaction.report.resource_statuses[@resource.to_s].should be_nil end end describe "when generating resources" do it "should call 'generate' on all created resources" do first = Puppet::Type.type(:notify).new(:name => "first") second = Puppet::Type.type(:notify).new(:name => "second") third = Puppet::Type.type(:notify).new(:name => "third") @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) first.expects(:generate).returns [second] second.expects(:generate).returns [third] third.expects(:generate) @transaction.generate_additional_resources(first) end it "should finish all resources" do generator = stub 'generator', :depthfirst? => true, :tags => [] resource = stub 'resource', :tag => nil @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.expects(:generate).returns [resource] @catalog.expects(:add_resource).yields(resource) resource.expects(:finish) @transaction.generate_additional_resources(generator) end it "should skip generated resources that conflict with existing resources" do generator = mock 'generator', :tags => [] resource = stub 'resource', :tag => nil @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.expects(:generate).returns [resource] @catalog.expects(:add_resource).raises(Puppet::Resource::Catalog::DuplicateResourceError.new("foo")) resource.expects(:finish).never resource.expects(:info) # log that it's skipped @transaction.generate_additional_resources(generator) end it "should copy all tags to the newly generated resources" do child = stub 'child' generator = stub 'resource', :tags => ["one", "two"] @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) generator.stubs(:generate).returns [child] @catalog.stubs(:add_resource) child.expects(:tag).with("one", "two") child.expects(:finish) generator.expects(:depthfirst?) @transaction.generate_additional_resources(generator) end end describe "when skipping a resource" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should skip resource with missing tags" do @transaction.stubs(:missing_tags?).returns(true) @transaction.should be_skip(@resource) end it "should skip unscheduled resources" do @transaction.stubs(:scheduled?).returns(false) @transaction.should be_skip(@resource) end it "should skip resources with failed dependencies" do @transaction.stubs(:failed_dependencies?).returns(true) @transaction.should be_skip(@resource) end it "should skip virtual resource" do @resource.stubs(:virtual?).returns true @transaction.should be_skip(@resource) end it "should skip device only resouce on normal host" do @resource.stubs(:appliable_to_device?).returns true @transaction.for_network_device = false @transaction.should be_skip(@resource) end it "should not skip device only resouce on remote device" do @resource.stubs(:appliable_to_device?).returns true @transaction.for_network_device = true @transaction.should_not be_skip(@resource) end it "should skip host resouce on device" do @resource.stubs(:appliable_to_device?).returns false @transaction.for_network_device = true @transaction.should be_skip(@resource) end end describe "when determining if tags are missing" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) @transaction.stubs(:ignore_tags?).returns false end it "should not be missing tags if tags are being ignored" do @transaction.expects(:ignore_tags?).returns true @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should not be missing tags if the transaction tags are empty" do @transaction.tags = [] @resource.expects(:tagged?).never @transaction.should_not be_missing_tags(@resource) end it "should otherwise let the resource determine if it is missing tags" do tags = ['one', 'two'] @transaction.tags = tags @resource.expects(:tagged?).with(*tags).returns(false) @transaction.should be_missing_tags(@resource) end end describe "when determining if a resource should be scheduled" do before :each do @resource = Puppet::Type.type(:notify).new :name => "foo" @catalog = Puppet::Resource::Catalog.new @resource.catalog = @catalog @transaction = Puppet::Transaction.new(@catalog) end it "should always schedule resources if 'ignoreschedules' is set" do @transaction.ignoreschedules = true @transaction.resource_harness.expects(:scheduled?).never @transaction.should be_scheduled(@resource) end it "should let the resource harness determine whether the resource should be scheduled" do @transaction.resource_harness.expects(:scheduled?).with(@transaction.resource_status(@resource), @resource).returns "feh" @transaction.scheduled?(@resource).should == "feh" end end describe "when prefetching" do it "should match resources by name, not title" do @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) # Have both a title and name resource = Puppet::Type.type(:sshkey).create :title => "foo", :name => "bar", :type => :dsa, :key => "eh" @catalog.add_resource resource resource.provider.class.expects(:prefetch).with("bar" => resource) @transaction.prefetch end end it "should return all resources for which the resource status indicates the resource has changed when determinig changed resources" do @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) names = [] 2.times do |i| name = File.join(@basepath, "file#{i}") resource = Puppet::Type.type(:file).new :path => name names << resource.to_s @catalog.add_resource resource @transaction.add_resource_status Puppet::Resource::Status.new(resource) end @transaction.resource_status(names[0]).changed = true @transaction.changed?.should == [@catalog.resource(names[0])] end describe 'when checking application run state' do before do without_warnings { Puppet::Application = Class.new(Puppet::Application) } @catalog = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@catalog) end after do without_warnings { Puppet::Application = Puppet::Application.superclass } end it 'should return true for :stop_processing? if Puppet::Application.stop_requested? is true' do Puppet::Application.stubs(:stop_requested?).returns(true) @transaction.stop_processing?.should be_true end it 'should return false for :stop_processing? if Puppet::Application.stop_requested? is false' do Puppet::Application.stubs(:stop_requested?).returns(false) @transaction.stop_processing?.should be_false end describe 'within an evaluate call' do before do @resource = Puppet::Type.type(:notify).new :title => "foobar" @catalog.add_resource @resource @transaction.stubs(:prepare) end it 'should stop processing if :stop_processing? is true' do @transaction.stubs(:stop_processing?).returns(true) @transaction.expects(:eval_resource).never @transaction.evaluate end it 'should continue processing if :stop_processing? is false' do @transaction.stubs(:stop_processing?).returns(false) @transaction.expects(:eval_resource).returns(nil) @transaction.evaluate end end end end describe Puppet::Transaction, " when determining tags" do before do @config = Puppet::Resource::Catalog.new @transaction = Puppet::Transaction.new(@config) end it "should default to the tags specified in the :tags setting" do Puppet.expects(:[]).with(:tags).returns("one") @transaction.tags.should == %w{one} end it "should split tags based on ','" do Puppet.expects(:[]).with(:tags).returns("one,two") @transaction.tags.should == %w{one two} end it "should use any tags set after creation" do Puppet.expects(:[]).with(:tags).never @transaction.tags = %w{one two} @transaction.tags.should == %w{one two} end it "should always convert assigned tags to an array" do @transaction.tags = "one::two" @transaction.tags.should == %w{one::two} end it "should accept a comma-delimited string" do @transaction.tags = "one, two" @transaction.tags.should == %w{one two} end it "should accept an empty string" do @transaction.tags = "" @transaction.tags.should == [] end end diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb index 47d1b8523..2861bb0e3 100755 --- a/spec/unit/type/exec_spec.rb +++ b/spec/unit/type/exec_spec.rb @@ -1,659 +1,666 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Type.type(:exec) do + include PuppetSpec::Files + def exec_tester(command, exitstatus = 0, rest = {}) @user_name = 'some_user_name' @group_name = 'some_group_name' Puppet.features.stubs(:root?).returns(true) output = rest.delete(:output) || '' tries = rest[:tries] || 1 args = { :name => command, :path => @example_path, :user => @user_name, :group => @group_name, :logoutput => false, :loglevel => :err, :returns => 0 }.merge(rest) exec = Puppet::Type.type(:exec).new(args) status = stub "process", :exitstatus => exitstatus Puppet::Util::SUIDManager.expects(:run_and_capture).times(tries). with([command], @user_name, @group_name).returns([output, status]) return exec end before do - @command = Puppet.features.posix? ? '/bin/true whatever' : '"C:/Program Files/something.exe" whatever' + @command = make_absolute('/bin/true whatever') + @executable = make_absolute('/bin/true') + @bogus_cmd = make_absolute('/bogus/cmd') end - describe "when not stubbing the provider" do + describe "when not stubbing the provider", :fails_on_windows => true do before do - @executable = Puppet.features.posix? ? '/bin/true' : 'C:/Program Files/something.exe' File.stubs(:exists?).returns false File.stubs(:exists?).with(@executable).returns true File.stubs(:exists?).with('/bin/false').returns true @example_path = Puppet.features.posix? ? %w{/usr/bin /bin} : [ "C:/Program Files/something/bin", "C:/Ruby/bin" ] File.stubs(:exists?).with(File.join(@example_path[0],"true")).returns true File.stubs(:exists?).with(File.join(@example_path[0],"false")).returns true end it "should return :executed_command as its event" do resource = Puppet::Type.type(:exec).new :command => @command resource.parameter(:returns).event.name.should == :executed_command end describe "when execing" do it "should use the 'run_and_capture' method to exec" do exec_tester("true").refresh.should == :executed_command end it "should report a failure" do proc { exec_tester('false', 1).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should not report a failure if the exit status is specified in a returns array" do proc { exec_tester("false", 1, :returns => [0, 1]).refresh }.should_not raise_error end it "should report a failure if the exit status is not specified in a returns array" do proc { exec_tester('false', 1, :returns => [0, 100]).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) end it "should log the output on success" do output = "output1\noutput2\n" exec_tester('false', 0, :output => output, :logoutput => true).refresh output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :logoutput => true).refresh }. should raise_error(Puppet::Error) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end end describe "when logoutput=>on_failure is set" do it "should log the output on failure" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :logoutput => :on_failure).refresh }. should raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "should log the output on failure when returns is specified as an array" do output = "output1\noutput2\n" proc { exec_tester('false', 1, :output => output, :returns => [0, 100], :logoutput => :on_failure).refresh }.should raise_error(Puppet::Error, /^false returned 1 instead of/) output.split("\n").each do |line| log = @logs.shift log.level.should == :err log.message.should == line end end it "shouldn't log the output on success" do exec_tester('true', 0, :output => "a\nb\nc\n", :logoutput => :on_failure).refresh @logs.should == [] end end it "shouldn't log the output on success when non-zero exit status is in a returns array" do exec_tester("true", 100, :output => "a\n", :logoutput => :on_failure, :returns => [1, 100]).refresh @logs.should == [] end describe " when multiple tries are set," do it "should repeat the command attempt 'tries' times on failure and produce an error" do tries = 5 resource = exec_tester("false", 1, :tries => tries, :try_sleep => 0) proc { resource.refresh }.should raise_error(Puppet::Error) end end end - it "should be able to autorequire files mentioned in the command" do + it "should be able to autorequire files mentioned in the command", :fails_on_windows => true do + foo = make_absolute('/bin/foo') catalog = Puppet::Resource::Catalog.new - tmp = Puppet::Type.type(:file).new(:name => "/bin/foo") + tmp = Puppet::Type.type(:file).new(:name => foo) catalog.add_resource tmp - execer = Puppet::Type.type(:exec).new(:name => "/bin/foo") + execer = Puppet::Type.type(:exec).new(:name => foo) catalog.add_resource execer catalog.relationship_graph.dependencies(execer).should == [tmp] end - describe "when handling the path parameter" do + describe "when handling the path parameter", :fails_on_windows => true do expect = %w{one two three four} { "an array" => expect, "a colon separated list" => "one:two:three:four", "a semi-colon separated list" => "one;two;three;four", "both array and colon lists" => ["one", "two:three", "four"], "both array and semi-colon lists" => ["one", "two;three", "four"], "colon and semi-colon lists" => ["one:two", "three;four"] }.each do |test, input| it "should accept #{test}" do type = Puppet::Type.type(:exec).new(:name => @command, :path => input) type[:path].should == expect end end end - describe "when setting user" do + describe "when setting user", :fails_on_windows => true do it "should fail if we are not root" do Puppet.features.stubs(:root?).returns(false) expect { Puppet::Type.type(:exec).new(:name => @command, :user => 'input') }. should raise_error Puppet::Error, /Parameter user failed/ end ['one', 2, 'root', 4294967295, 4294967296].each do |value| it "should accept '#{value}' as user if we are root" do Puppet.features.stubs(:root?).returns(true) type = Puppet::Type.type(:exec).new(:name => @command, :user => value) type[:user].should == value end end end describe "when setting group" do shared_examples_for "exec[:group]" do ['one', 2, 'wheel', 4294967295, 4294967296].each do |value| - it "should accept '#{value}' without error or judgement" do + it "should accept '#{value}' without error or judgement", :fails_on_windows => true do type = Puppet::Type.type(:exec).new(:name => @command, :group => value) type[:group].should == value end end end describe "when running as root" do before :each do Puppet.features.stubs(:root?).returns(true) end it_behaves_like "exec[:group]" end describe "when not running as root" do before :each do Puppet.features.stubs(:root?).returns(false) end it_behaves_like "exec[:group]" end end describe "when setting cwd" do it_should_behave_like "all path parameters", :cwd, :array => false do def instance(path) - Puppet::Type.type(:exec).new(:name => '/bin/true', :cwd => path) + Puppet::Type.type(:exec).new(:name => @executable, :cwd => path) end end end shared_examples_for "all exec command parameters" do |param| { "relative" => "example", "absolute" => "/bin/example" }.sort.each do |name, command| describe "if command is #{name}" do before :each do @param = param end def test(command, valid) if @param == :name then instance = Puppet::Type.type(:exec).new() else - instance = Puppet::Type.type(:exec).new(:name => "/bin/true") + instance = Puppet::Type.type(:exec).new(:name => @executable) end if valid then instance.provider.expects(:validatecmd).returns(true) else instance.provider.expects(:validatecmd).raises(Puppet::Error, "from a stub") end instance[@param] = command end it "should work if the provider calls the command valid" do expect { test(command, true) }.should_not raise_error end it "should fail if the provider calls the command invalid" do expect { test(command, false) }. should raise_error Puppet::Error, /Parameter #{@param} failed: from a stub/ end end end end shared_examples_for "all exec command parameters that take arrays" do |param| describe "when given an array of inputs" do before :each do - @test = Puppet::Type.type(:exec).new(:name => "/bin/true") + @test = Puppet::Type.type(:exec).new(:name => @executable) end it "should accept the array when all commands return valid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(true) @test[param] = input @test[param].should == input end it "should reject the array when any commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).with(input.first).returns(false) input[1..-1].each do |cmd| @test.provider.expects(:validatecmd).with(cmd).returns(true) end @test[param] = input @test[param].should == input end it "should reject the array when all commands return invalid" do input = %w{one two three} @test.provider.expects(:validatecmd).times(input.length).returns(false) @test[param] = input @test[param].should == input end end end describe "when setting refresh" do it_should_behave_like "all exec command parameters", :refresh end describe "for simple parameters" do before :each do - @exec = Puppet::Type.type(:exec).new(:name => '/bin/true') + @exec = Puppet::Type.type(:exec).new(:name => @executable) end describe "when setting environment" do { "single values" => "foo=bar", "multiple values" => ["foo=bar", "baz=quux"], }.each do |name, data| it "should accept #{name}" do @exec[:environment] = data @exec[:environment].should == data end end { "single values" => "foo", "only values" => ["foo", "bar"], "any values" => ["foo=bar", "baz"] }.each do |name, data| it "should reject #{name} without assignment" do expect { @exec[:environment] = data }. should raise_error Puppet::Error, /Invalid environment setting/ end end end describe "when setting timeout" do [0, 0.1, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:timeout] = valid @exec[:timeout].should == valid end it "should accept '#{valid}' in an array as valid" do @exec[:timeout] = [valid] @exec[:timeout].should == valid end end ['1/2', '', 'foo', '5foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:timeout] = invalid }. should raise_error Puppet::Error, /The timeout must be a number/ end it "should reject '#{invalid}' in an array as invalid" do expect { @exec[:timeout] = [invalid] }. should raise_error Puppet::Error, /The timeout must be a number/ end end it "should fail if timeout is exceeded" do File.stubs(:exists?).with('/bin/sleep').returns(true) File.stubs(:exists?).with('sleep').returns(false) sleep_exec = Puppet::Type.type(:exec).new(:name => 'sleep 1', :path => ['/bin'], :timeout => '0.2') lambda { sleep_exec.refresh }.should raise_error Puppet::Error, "Command exceeded timeout" end it "should convert timeout to a float" do - resource = Puppet::Type.type(:exec).new :command => "/bin/false", :timeout => "12" + command = make_absolute('/bin/false') + resource = Puppet::Type.type(:exec).new :command => command, :timeout => "12" resource[:timeout].should be_a(Float) resource[:timeout].should == 12.0 end it "should munge negative timeouts to 0.0" do - resource = Puppet::Type.type(:exec).new :command => "/bin/false", :timeout => "-12.0" + command = make_absolute('/bin/false') + resource = Puppet::Type.type(:exec).new :command => command, :timeout => "-12.0" resource.parameter(:timeout).value.should be_a(Float) resource.parameter(:timeout).value.should == 0.0 end end describe "when setting tries" do [1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:tries] = valid @exec[:tries].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:tries] = [valid] @exec[:tries].should == valid end end end [-3.5, -1, 0, 0.2, '1/2', '1_000_000', '+12', '', 'foo'].each do |invalid| it "should reject '#{invalid}' as invalid" do expect { @exec[:tries] = invalid }. should raise_error Puppet::Error, /Tries must be an integer/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:tries] = [invalid] }. should raise_error Puppet::Error, /Tries must be an integer/ end end end end describe "when setting try_sleep" do [0, 0.2, 1, 10, 4294967295].each do |valid| it "should accept '#{valid}' as valid" do @exec[:try_sleep] = valid @exec[:try_sleep].should == valid end if "REVISIT: too much test log spam" == "a good thing" then it "should accept '#{valid}' in an array as valid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" @exec[:try_sleep] = [valid] @exec[:try_sleep].should == valid end end end { -3.5 => "cannot be a negative number", -1 => "cannot be a negative number", '1/2' => 'must be a number', '1_000_000' => 'must be a number', '+12' => 'must be a number', '' => 'must be a number', 'foo' => 'must be a number', }.each do |invalid, error| it "should reject '#{invalid}' as invalid" do expect { @exec[:try_sleep] = invalid }. should raise_error Puppet::Error, /try_sleep #{error}/ end if "REVISIT: too much test log spam" == "a good thing" then it "should reject '#{invalid}' in an array as invalid" do pending "inconsistent, but this is not supporting arrays, unlike timeout" expect { @exec[:try_sleep] = [invalid] }. should raise_error Puppet::Error, /try_sleep #{error}/ end end end end describe "when setting refreshonly" do [:true, :false].each do |value| it "should accept '#{value}'" do @exec[:refreshonly] = value @exec[:refreshonly].should == value end end [1, 0, "1", "0", "yes", "y", "no", "n"].each do |value| it "should reject '#{value}'" do expect { @exec[:refreshonly] = value }. should raise_error(Puppet::Error, /Invalid value #{value.inspect}\. Valid values are true, false/ ) end end end describe "when setting creates" do it_should_behave_like "all path parameters", :creates, :array => true do def instance(path) - Puppet::Type.type(:exec).new(:name => '/bin/true', :creates => path) + Puppet::Type.type(:exec).new(:name => @executable, :creates => path) end end end end describe "when setting unless" do it_should_behave_like "all exec command parameters", :unless it_should_behave_like "all exec command parameters that take arrays", :unless end describe "when setting onlyif" do it_should_behave_like "all exec command parameters", :onlyif it_should_behave_like "all exec command parameters that take arrays", :onlyif end describe "#check" do before :each do - @test = Puppet::Type.type(:exec).new(:name => "/bin/true") + @test = Puppet::Type.type(:exec).new(:name => @executable) end describe ":refreshonly" do { :true => false, :false => true }.each do |input, result| it "should return '#{result}' when given '#{input}'" do @test[:refreshonly] = input @test.check_all_attributes.should == result end end end describe ":creates" do before :all do @exist = "/" @unexist = "/this/path/should/never/exist" while FileTest.exist?(@unexist) do @unexist += "/foo" end end context "with a single item" do it "should run when the item does not exist" do @test[:creates] = @unexist @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = @exist @test.check_all_attributes.should == false end end context "with an array with one item" do it "should run when the item does not exist" do @test[:creates] = [@unexist] @test.check_all_attributes.should == true end it "should not run when the item exists" do @test[:creates] = [@exist] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run when all items do not exist" do @test[:creates] = [@unexist] * 3 @test.check_all_attributes.should == true end it "should not run when one item exists" do @test[:creates] = [@unexist, @exist, @unexist] @test.check_all_attributes.should == false end it "should not run when all items exist" do @test[:creates] = [@exist] * 3 end end end { :onlyif => { :pass => false, :fail => true }, :unless => { :pass => true, :fail => false }, }.each do |param, sense| describe ":#{param}" do before :each do - @pass = "/magic/pass" - @fail = "/magic/fail" + @pass = make_absolute("/magic/pass") + @fail = make_absolute("/magic/fail") @pass_status = stub('status', :exitstatus => sense[:pass] ? 0 : 1) @fail_status = stub('status', :exitstatus => sense[:fail] ? 0 : 1) @test.provider.stubs(:checkexe).returns(true) [true, false].each do |check| @test.provider.stubs(:run).with(@pass, check). returns(['test output', @pass_status]) @test.provider.stubs(:run).with(@fail, check). returns(['test output', @fail_status]) end end context "with a single item" do it "should run if the command exits non-zero" do @test[param] = @fail @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = @pass @test.check_all_attributes.should == false end end context "with an array with a single item" do it "should run if the command exits non-zero" do @test[param] = [@fail] @test.check_all_attributes.should == true end it "should not run if the command exits zero" do @test[param] = [@pass] @test.check_all_attributes.should == false end end context "with an array with multiple items" do it "should run if all the commands exits non-zero" do @test[param] = [@fail] * 3 @test.check_all_attributes.should == true end it "should not run if one command exits zero" do @test[param] = [@pass, @fail, @pass] @test.check_all_attributes.should == false end it "should not run if all command exits zero" do @test[param] = [@pass] * 3 @test.check_all_attributes.should == false end end end end end - describe "#retrieve" do + describe "#retrieve", :fails_on_windows => true do before :each do - @exec_resource = Puppet::Type.type(:exec).new(:name => "/bogus/cmd") + @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should return :notrun when check_all_attributes returns true" do @exec_resource.stubs(:check_all_attributes).returns true @exec_resource.retrieve[:returns].should == :notrun end it "should return default exit code 0 when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource.retrieve[:returns].should == ['0'] end it "should return the specified exit code when check_all_attributes returns false" do @exec_resource.stubs(:check_all_attributes).returns false @exec_resource[:returns] = 42 @exec_resource.retrieve[:returns].should == ["42"] end end describe "#output" do before :each do - @exec_resource = Puppet::Type.type(:exec).new(:name => "/bogus/cmd") + @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should return the provider's run output" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") provider.expects(:run).returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh @exec_resource.output.should == 'silly output' end end describe "#refresh" do before :each do - @exec_resource = Puppet::Type.type(:exec).new(:name => "/bogus/cmd") + @exec_resource = Puppet::Type.type(:exec).new(:name => @bogus_cmd) end it "should call provider run with the refresh parameter if it is set" do + myother_bogus_cmd = make_absolute('/myother/bogus/cmd') provider = stub 'provider' @exec_resource.stubs(:provider).returns(provider) - @exec_resource.stubs(:[]).with(:refresh).returns('/myother/bogus/cmd') - provider.expects(:run).with('/myother/bogus/cmd') + @exec_resource.stubs(:[]).with(:refresh).returns(myother_bogus_cmd) + provider.expects(:run).with(myother_bogus_cmd) @exec_resource.refresh end it "should call provider run with the specified command if the refresh parameter is not set" do provider = stub 'provider' status = stubs "process_status" status.stubs(:exitstatus).returns("0") - provider.expects(:run).with('/bogus/cmd').returns(["silly output", status]) + provider.expects(:run).with(@bogus_cmd).returns(["silly output", status]) @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end it "should not run the provider if check_all_attributes is false" do @exec_resource.stubs(:check_all_attributes).returns false provider = stub 'provider' provider.expects(:run).never @exec_resource.stubs(:provider).returns(provider) @exec_resource.refresh end end end diff --git a/spec/unit/type/file/checksum_spec.rb b/spec/unit/type/file/checksum_spec.rb index b47f617cc..30c4aba6d 100755 --- a/spec/unit/type/file/checksum_spec.rb +++ b/spec/unit/type/file/checksum_spec.rb @@ -1,72 +1,73 @@ #!/usr/bin/env rspec require 'spec_helper' checksum = Puppet::Type.type(:file).attrclass(:checksum) describe checksum do before do - @resource = Puppet::Type.type(:file).new :path => "/foo/bar" + @path = Puppet.features.microsoft_windows? ? "c:/foo/bar" : "/foo/bar" + @resource = Puppet::Type.type(:file).new :path => @path @checksum = @resource.parameter(:checksum) end it "should be a parameter" do checksum.superclass.must == Puppet::Parameter end it "should use its current value when asked to sum content" do @checksum.value = :md5lite @checksum.expects(:md5lite).with("foobar").returns "yay" @checksum.sum("foobar") end it "should use :md5 to sum when no value is set" do @checksum.expects(:md5).with("foobar").returns "yay" @checksum.sum("foobar") end it "should return the summed contents with a checksum label" do sum = Digest::MD5.hexdigest("foobar") @resource[:checksum] = :md5 @checksum.sum("foobar").should == "{md5}#{sum}" end it "should use :md5 as its default type" do @checksum.default.should == :md5 end it "should use its current value when asked to sum a file's content" do @checksum.value = :md5lite - @checksum.expects(:md5lite_file).with("/foo/bar").returns "yay" - @checksum.sum_file("/foo/bar") + @checksum.expects(:md5lite_file).with(@path).returns "yay" + @checksum.sum_file(@path) end it "should use :md5 to sum a file when no value is set" do - @checksum.expects(:md5_file).with("/foo/bar").returns "yay" - @checksum.sum_file("/foo/bar") + @checksum.expects(:md5_file).with(@path).returns "yay" + @checksum.sum_file(@path) end it "should convert all sums to strings when summing files" do @checksum.value = :mtime - @checksum.expects(:mtime_file).with("/foo/bar").returns Time.now - lambda { @checksum.sum_file("/foo/bar") }.should_not raise_error + @checksum.expects(:mtime_file).with(@path).returns Time.now + lambda { @checksum.sum_file(@path) }.should_not raise_error end it "should return the summed contents of a file with a checksum label" do @resource[:checksum] = :md5 @checksum.expects(:md5_file).returns "mysum" - @checksum.sum_file("/foo/bar").should == "{md5}mysum" + @checksum.sum_file(@path).should == "{md5}mysum" end it "should return the summed contents of a stream with a checksum label" do @resource[:checksum] = :md5 @checksum.expects(:md5_stream).returns "mysum" @checksum.sum_stream.should == "{md5}mysum" end it "should yield the sum_stream block to the underlying checksum" do @resource[:checksum] = :md5 @checksum.expects(:md5_stream).yields("something").returns("mysum") @checksum.sum_stream do |sum| sum.should == "something" end end end diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb index 7af5f9d83..04ec48555 100755 --- a/spec/unit/type/file/content_spec.rb +++ b/spec/unit/type/file/content_spec.rb @@ -1,435 +1,436 @@ #!/usr/bin/env rspec require 'spec_helper' content = Puppet::Type.type(:file).attrclass(:content) describe content do include PuppetSpec::Files before do @filename = tmpfile('testfile') @resource = Puppet::Type.type(:file).new :path => @filename File.open(@filename, 'w') {|f| f.write "initial file content"} content.stubs(:standalone?).returns(false) end describe "when determining the checksum type" do it "should use the type specified in the source checksum if a source is set" do @resource[:source] = "/foo" @resource.parameter(:source).expects(:checksum).returns "{md5lite}eh" @content = content.new(:resource => @resource) @content.checksum_type.should == :md5lite end it "should use the type specified by the checksum parameter if no source is set" do @resource[:checksum] = :md5lite @content = content.new(:resource => @resource) @content.checksum_type.should == :md5lite end end describe "when determining the actual content to write" do it "should use the set content if available" do @content = content.new(:resource => @resource) @content.should = "ehness" @content.actual_content.should == "ehness" end it "should not use the content from the source if the source is set" do source = mock 'source' @resource.expects(:parameter).never.with(:source).returns source @content = content.new(:resource => @resource) @content.actual_content.should be_nil end end describe "when setting the desired content" do it "should make the actual content available via an attribute" do @content = content.new(:resource => @resource) @content.stubs(:checksum_type).returns "md5" @content.should = "this is some content" @content.actual_content.should == "this is some content" end it "should store the checksum as the desired content" do @content = content.new(:resource => @resource) digest = Digest::MD5.hexdigest("this is some content") @content.stubs(:checksum_type).returns "md5" @content.should = "this is some content" @content.should.must == "{md5}#{digest}" end it "should not checksum 'absent'" do @content = content.new(:resource => @resource) @content.should = :absent @content.should.must == :absent end it "should accept a checksum as the desired content" do @content = content.new(:resource => @resource) digest = Digest::MD5.hexdigest("this is some content") string = "{md5}#{digest}" @content.should = string @content.should.must == string end end describe "when retrieving the current content" do it "should return :absent if the file does not exist" do @content = content.new(:resource => @resource) @resource.expects(:stat).returns nil @content.retrieve.should == :absent end it "should not manage content on directories" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "directory" @resource.expects(:stat).returns stat @content.retrieve.should be_nil end it "should not manage content on links" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "link" @resource.expects(:stat).returns stat @content.retrieve.should be_nil end it "should always return the checksum as a string" do @content = content.new(:resource => @resource) @resource[:checksum] = :mtime stat = mock 'stat', :ftype => "file" @resource.expects(:stat).returns stat time = Time.now @resource.parameter(:checksum).expects(:mtime_file).with(@resource[:path]).returns time @content.retrieve.should == "{mtime}#{time}" end it "should return the checksum of the file if it exists and is a normal file" do @content = content.new(:resource => @resource) stat = mock 'stat', :ftype => "file" @resource.expects(:stat).returns stat @resource.parameter(:checksum).expects(:md5_file).with(@resource[:path]).returns "mysum" @content.retrieve.should == "{md5}mysum" end end describe "when testing whether the content is in sync" do before do @resource[:ensure] = :file @content = content.new(:resource => @resource) end it "should return true if the resource shouldn't be a regular file" do @resource.expects(:should_be_file?).returns false @content.should = "foo" @content.must be_safe_insync("whatever") end it "should return false if the current content is :absent" do @content.should = "foo" @content.should_not be_safe_insync(:absent) end it "should return false if the file should be a file but is not present" do @resource.expects(:should_be_file?).returns true @content.should = "foo" @content.should_not be_safe_insync(:absent) end describe "and the file exists" do before do @resource.stubs(:stat).returns mock("stat") end it "should return false if the current contents are different from the desired content" do @content.should = "some content" @content.should_not be_safe_insync("other content") end it "should return true if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" @content.must be_safe_insync("{md5}" + Digest::MD5.hexdigest("some content")) end describe "and Puppet[:show_diff] is set" do before do Puppet[:show_diff] = true end it "should display a diff if the current contents are different from the desired content" do @content.should = "some content" @content.expects(:diff).returns("my diff").once @content.expects(:print).with("my diff").once @content.safe_insync?("other content") end it "should not display a diff if the sum for the current contents is the same as the sum for the desired content" do @content.should = "some content" @content.expects(:diff).never @content.safe_insync?("{md5}" + Digest::MD5.hexdigest("some content")) end end end describe "and :replace is false" do before do @resource.stubs(:replace?).returns false end it "should be insync if the file exists and the content is different" do @resource.stubs(:stat).returns mock('stat') @content.must be_safe_insync("whatever") end it "should be insync if the file exists and the content is right" do @resource.stubs(:stat).returns mock('stat') @content.must be_safe_insync("something") end it "should not be insync if the file does not exist" do @content.should = "foo" @content.should_not be_safe_insync(:absent) end end end describe "when changing the content" do before do @content = content.new(:resource => @resource) @content.should = "some content" @resource.stubs(:[]).with(:path).returns "/boo" @resource.stubs(:stat).returns "eh" end it "should use the file's :write method to write the content" do @resource.expects(:write).with(:content) @content.sync end it "should return :file_changed if the file already existed" do @resource.expects(:stat).returns "something" @resource.stubs(:write) @content.sync.should == :file_changed end it "should return :file_created if the file did not exist" do @resource.expects(:stat).returns nil @resource.stubs(:write) @content.sync.should == :file_created end end describe "when writing" do before do @content = content.new(:resource => @resource) end it "should attempt to read from the filebucket if no actual content nor source exists" do @fh = File.open(@filename, 'w') @content.should = "{md5}foo" @content.resource.bucket.class.any_instance.stubs(:getfile).returns "foo" @content.write(@fh) + @fh.close end describe "from actual content" do before(:each) do @content.stubs(:actual_content).returns("this is content") end it "should write to the given file handle" do @fh.expects(:print).with("this is content") @content.write(@fh) end it "should return the current checksum value" do @resource.parameter(:checksum).expects(:sum_stream).returns "checksum" @content.write(@fh).should == "checksum" end end describe "from a file bucket" do it "should fail if a file bucket cannot be retrieved" do @content.should = "{md5}foo" @content.resource.expects(:bucket).returns nil lambda { @content.write(@fh) }.should raise_error(Puppet::Error) end it "should fail if the file bucket cannot find any content" do @content.should = "{md5}foo" bucket = stub 'bucket' @content.resource.expects(:bucket).returns bucket bucket.expects(:getfile).with("foo").raises "foobar" lambda { @content.write(@fh) }.should raise_error(Puppet::Error) end it "should write the returned content to the file" do @content.should = "{md5}foo" bucket = stub 'bucket' @content.resource.expects(:bucket).returns bucket bucket.expects(:getfile).with("foo").returns "mycontent" @fh.expects(:print).with("mycontent") @content.write(@fh) end end - describe "from local source" do + describe "from local source", :fails_on_windows => true do before(:each) do @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false @sourcename = tmpfile('source') @source_content = "source file content"*10000 @sourcefile = File.open(@sourcename, 'w') {|f| f.write @source_content} @content = @resource.newattr(:content) @source = @resource.newattr(:source) @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') end it "should copy content from the source to the file" do @resource.write(@source) File.read(@filename).should == @source_content end it "should return the checksum computed" do File.open(@filename, 'w') do |file| @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" end end end describe "from remote source" do before(:each) do @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false @response = stub_everything 'response', :code => "200" @source_content = "source file content"*10000 @response.stubs(:read_body).multiple_yields(*(["source file content"]*10000)) @conn = stub_everything 'connection' @conn.stubs(:request_get).yields(@response) Puppet::Network::HttpPool.stubs(:http_instance).returns @conn @content = @resource.newattr(:content) @sourcename = "puppet:///test/foo" @source = @resource.newattr(:source) @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') end it "should write the contents to the file" do @resource.write(@source) File.read(@filename).should == @source_content end it "should not write anything if source is not found" do @response.stubs(:code).returns("404") lambda {@resource.write(@source)}.should raise_error(Net::HTTPError) { |e| e.message =~ /404/ } File.read(@filename).should == "initial file content" end it "should raise an HTTP error in case of server error" do @response.stubs(:code).returns("500") lambda { @content.write(@fh) }.should raise_error { |e| e.message.include? @source_content } end it "should return the checksum computed" do File.open(@filename, 'w') do |file| @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" end end end # These are testing the implementation rather than the desired behaviour; while that bites, there are a whole # pile of other methods in the File type that depend on intimate details of this implementation and vice-versa. # If these blow up, you are gonna have to review the callers to make sure they don't explode! --daniel 2011-02-01 describe "each_chunk_from should work" do before do @content = content.new(:resource => @resource) end it "when content is a string" do @content.each_chunk_from('i_am_a_string') { |chunk| chunk.should == 'i_am_a_string' } end # The following manifest is a case where source and content.should are both set # file { "/tmp/mydir" : # source => '/tmp/sourcedir', # recurse => true, # } it "when content checksum comes from source" do source_param = Puppet::Type.type(:file).attrclass(:source) source = source_param.new(:resource => @resource) @content.should = "{md5}123abcd" @content.expects(:chunk_file_from_source).returns('from_source') @content.each_chunk_from(source) { |chunk| chunk.should == 'from_source' } end it "when no content, source, but ensure present" do @resource[:ensure] = :present @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end # you might do this if you were just auditing it "when no content, source, but ensure file" do @resource[:ensure] = :file @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end it "when source_or_content is nil and content not a checksum" do @content.each_chunk_from(nil) { |chunk| chunk.should == '' } end # the content is munged so that if it's a checksum nil gets passed in it "when content is a checksum it should try to read from filebucket" do @content.should = "{md5}123abcd" @content.expects(:read_file_from_filebucket).once.returns('im_a_filebucket') @content.each_chunk_from(nil) { |chunk| chunk.should == 'im_a_filebucket' } end it "when running as puppet apply" do @content.class.expects(:standalone?).returns true source_or_content = stubs('source_or_content') source_or_content.expects(:content).once.returns :whoo @content.each_chunk_from(source_or_content) { |chunk| chunk.should == :whoo } end it "when running from source with a local file" do source_or_content = stubs('source_or_content') source_or_content.expects(:local?).returns true @content.expects(:chunk_file_from_disk).with(source_or_content).once.yields 'woot' @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } end it "when running from source with a remote file" do source_or_content = stubs('source_or_content') source_or_content.expects(:local?).returns false @content.expects(:chunk_file_from_source).with(source_or_content).once.yields 'woot' @content.each_chunk_from(source_or_content) { |chunk| chunk.should == 'woot' } end end end end diff --git a/spec/unit/type/file/selinux_spec.rb b/spec/unit/type/file/selinux_spec.rb index 2622948d0..f6e7451c7 100755 --- a/spec/unit/type/file/selinux_spec.rb +++ b/spec/unit/type/file/selinux_spec.rb @@ -1,87 +1,89 @@ #!/usr/bin/env rspec require 'spec_helper' - [:seluser, :selrole, :seltype, :selrange].each do |param| property = Puppet::Type.type(:file).attrclass(param) describe property do + include PuppetSpec::Files + before do - @resource = Puppet::Type.type(:file).new :path => "/my/file" + @path = make_absolute("/my/file") + @resource = Puppet::Type.type(:file).new :path => @path @sel = property.new :resource => @resource end it "retrieve on #{param} should return :absent if the file isn't statable" do @resource.expects(:stat).returns nil @sel.retrieve.should == :absent end it "should retrieve nil for #{param} if there is no SELinux support" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat - @sel.expects(:get_selinux_current_context).with("/my/file").returns nil + @sel.expects(:get_selinux_current_context).with(@path).returns nil @sel.retrieve.should be_nil end it "should retrieve #{param} if a SELinux context is found with a range" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat - @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t:s0" + @sel.expects(:get_selinux_current_context).with(@path).returns "user_u:role_r:type_t:s0" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; "s0" end @sel.retrieve.should == expectedresult end it "should retrieve #{param} if a SELinux context is found without a range" do stat = stub 'stat', :ftype => "foo" @resource.expects(:stat).returns stat - @sel.expects(:get_selinux_current_context).with("/my/file").returns "user_u:role_r:type_t" + @sel.expects(:get_selinux_current_context).with(@path).returns "user_u:role_r:type_t" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; nil end @sel.retrieve.should == expectedresult end it "should handle no default gracefully" do - @sel.expects(:get_selinux_default_context).with("/my/file").returns nil + @sel.expects(:get_selinux_default_context).with(@path).returns nil @sel.default.must be_nil end it "should be able to detect matchpathcon defaults" do @sel.stubs(:debug) - @sel.expects(:get_selinux_default_context).with("/my/file").returns "user_u:role_r:type_t:s0" + @sel.expects(:get_selinux_default_context).with(@path).returns "user_u:role_r:type_t:s0" expectedresult = case param when :seluser; "user_u" when :selrole; "role_r" when :seltype; "type_t" when :selrange; "s0" end @sel.default.must == expectedresult end it "should return nil for defaults if selinux_ignore_defaults is true" do @resource[:selinux_ignore_defaults] = :true @sel.default.must be_nil end it "should be able to set a new context" do stat = stub 'stat', :ftype => "foo" @sel.should = %w{newone} - @sel.expects(:set_selinux_context).with("/my/file", ["newone"], param) + @sel.expects(:set_selinux_context).with(@path, ["newone"], param) @sel.sync end it "should do nothing for safe_insync? if no SELinux support" do @sel.should = %{newcontext} @sel.expects(:selinux_support?).returns false @sel.safe_insync?("oldcontext").should == true end end end diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb index 5665d323d..5129d1e01 100755 --- a/spec/unit/type/file/source_spec.rb +++ b/spec/unit/type/file/source_spec.rb @@ -1,272 +1,276 @@ #!/usr/bin/env rspec require 'spec_helper' source = Puppet::Type.type(:file).attrclass(:source) describe Puppet::Type.type(:file).attrclass(:source) do + include PuppetSpec::Files + before do # Wow that's a messy interface to the resource. @resource = stub 'resource', :[]= => nil, :property => nil, :catalog => stub("catalog", :dependent_data_expired? => false), :line => 0, :file => '' + @foobar = make_absolute("/foo/bar") + @feebooz = make_absolute("/fee/booz") end it "should be a subclass of Parameter" do source.superclass.must == Puppet::Parameter end describe "when initializing" do it "should fail if the set values are not URLs" do s = source.new(:resource => @resource) URI.expects(:parse).with('foo').raises RuntimeError lambda { s.value = %w{foo} }.must raise_error(Puppet::Error) end it "should fail if the URI is not a local file, file URI, or puppet URI" do s = source.new(:resource => @resource) lambda { s.value = %w{http://foo/bar} }.must raise_error(Puppet::Error) end end it "should have a method for retrieving its metadata" do source.new(:resource => @resource).must respond_to(:metadata) end it "should have a method for setting its metadata" do source.new(:resource => @resource).must respond_to(:metadata=) end - describe "when returning the metadata" do + describe "when returning the metadata", :fails_on_windows => true do before do @metadata = stub 'metadata', :source= => nil end it "should return already-available metadata" do @source = source.new(:resource => @resource) @source.metadata = "foo" @source.metadata.should == "foo" end it "should return nil if no @should value is set and no metadata is available" do @source = source.new(:resource => @resource) @source.metadata.should be_nil end it "should collect its metadata using the Metadata class if it is not already set" do - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns @metadata + @source = source.new(:resource => @resource, :value => @foobar) + Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar).returns @metadata @source.metadata end it "should use the metadata from the first found source" do metadata = stub 'metadata', :source= => nil - @source = source.new(:resource => @resource, :value => ["/foo/bar", "/fee/booz"]) - Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns nil - Puppet::FileServing::Metadata.indirection.expects(:find).with("/fee/booz").returns metadata + @source = source.new(:resource => @resource, :value => [@foobar, @feebooz]) + Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar).returns nil + Puppet::FileServing::Metadata.indirection.expects(:find).with(@feebooz).returns metadata @source.metadata.should equal(metadata) end it "should store the found source as the metadata's source" do metadata = mock 'metadata' - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns metadata + @source = source.new(:resource => @resource, :value => @foobar) + Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar).returns metadata - metadata.expects(:source=).with("/foo/bar") + metadata.expects(:source=).with(@foobar) @source.metadata end it "should fail intelligently if an exception is encountered while querying for metadata" do - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").raises RuntimeError + @source = source.new(:resource => @resource, :value => @foobar) + Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar).raises RuntimeError @source.expects(:fail).raises ArgumentError lambda { @source.metadata }.should raise_error(ArgumentError) end it "should fail if no specified sources can be found" do - @source = source.new(:resource => @resource, :value => "/foo/bar") - Puppet::FileServing::Metadata.indirection.expects(:find).with("/foo/bar").returns nil + @source = source.new(:resource => @resource, :value => @foobar) + Puppet::FileServing::Metadata.indirection.expects(:find).with(@foobar).returns nil @source.expects(:fail).raises RuntimeError lambda { @source.metadata }.should raise_error(RuntimeError) end it "should expire the metadata appropriately" do expirer = stub 'expired', :dependent_data_expired? => true metadata = stub 'metadata', :source= => nil - Puppet::FileServing::Metadata.indirection.expects(:find).with("/fee/booz").returns metadata + Puppet::FileServing::Metadata.indirection.expects(:find).with(@feebooz).returns metadata - @source = source.new(:resource => @resource, :value => ["/fee/booz"]) + @source = source.new(:resource => @resource, :value => [@feebooz]) @source.metadata = "foo" @source.stubs(:expirer).returns expirer @source.metadata.should_not == "foo" end end it "should have a method for setting the desired values on the resource" do source.new(:resource => @resource).must respond_to(:copy_source_values) end describe "when copying the source values" do before do - @resource = Puppet::Type.type(:file).new :path => "/foo/bar" + @resource = Puppet::Type.type(:file).new :path => @foobar @source = source.new(:resource => @resource) @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => 123, :checksum => "{md5}asdfasdf", :ftype => "file" @source.stubs(:metadata).returns @metadata end it "should fail if there is no metadata" do @source.stubs(:metadata).returns nil @source.expects(:devfail).raises ArgumentError lambda { @source.copy_source_values }.should raise_error(ArgumentError) end it "should set :ensure to the file type" do @metadata.stubs(:ftype).returns "file" @source.copy_source_values @resource[:ensure].must == :file end it "should not set 'ensure' if it is already set to 'absent'" do @metadata.stubs(:ftype).returns "file" @resource[:ensure] = :absent @source.copy_source_values @resource[:ensure].must == :absent end describe "and the source is a file" do before do @metadata.stubs(:ftype).returns "file" end it "should copy the metadata's owner, group, checksum, and mode to the resource if they are not set on the resource" do Puppet.features.expects(:root?).returns true @source.copy_source_values @resource[:owner].must == 100 @resource[:group].must == 200 @resource[:mode].must == "173" # Metadata calls it checksum, we call it content. @resource[:content].must == @metadata.checksum end it "should not copy the metadata's owner to the resource if it is already set" do @resource[:owner] = 1 @resource[:group] = 2 @resource[:mode] = 3 @resource[:content] = "foobar" @source.copy_source_values @resource[:owner].must == 1 @resource[:group].must == 2 @resource[:mode].must == "3" @resource[:content].should_not == @metadata.checksum end describe "and puppet is not running as root" do it "should not try to set the owner" do Puppet.features.expects(:root?).returns false @source.copy_source_values @resource[:owner].should be_nil end end end describe "and the source is a link" do it "should set the target to the link destination" do @metadata.stubs(:ftype).returns "link" @metadata.stubs(:links).returns "manage" @resource.stubs(:[]) @resource.stubs(:[]=) @metadata.expects(:destination).returns "/path/to/symlink" @resource.expects(:[]=).with(:target, "/path/to/symlink") @source.copy_source_values end end end it "should have a local? method" do source.new(:resource => @resource).must be_respond_to(:local?) end context "when accessing source properties" do before(:each) do @source = source.new(:resource => @resource) @metadata = stub_everything @source.stubs(:metadata).returns(@metadata) end describe "for local sources" do before(:each) do @metadata.stubs(:ftype).returns "file" @metadata.stubs(:source).returns("file:///path/to/source") end it "should be local" do @source.must be_local end it "should be local if there is no scheme" do @metadata.stubs(:source).returns("/path/to/source") @source.must be_local end it "should be able to return the metadata source full path" do @source.full_path.should == "/path/to/source" end end describe "for remote sources" do before(:each) do @metadata.stubs(:ftype).returns "file" @metadata.stubs(:source).returns("puppet://server:8192/path/to/source") end it "should not be local" do @source.should_not be_local end it "should be able to return the metadata source full path" do @source.full_path.should == "/path/to/source" end it "should be able to return the source server" do @source.server.should == "server" end it "should be able to return the source port" do @source.port.should == 8192 end describe "which don't specify server or port" do before(:each) do @metadata.stubs(:source).returns("puppet:///path/to/source") end it "should return the default source server" do Puppet.settings.expects(:[]).with(:server).returns("myserver") @source.server.should == "myserver" end it "should return the default source port" do Puppet.settings.expects(:[]).with(:masterport).returns(1234) @source.port.should == 1234 end end end end end diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb index 3a01d09c1..f416e987a 100755 --- a/spec/unit/type/file_spec.rb +++ b/spec/unit/type/file_spec.rb @@ -1,1241 +1,1241 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Type.type(:file) do before do Puppet.settings.stubs(:use) @real_posix = Puppet.features.posix? Puppet.features.stubs("posix?").returns(true) @path = Tempfile.new("puppetspec") pathname = @path.path @path.close!() @path = pathname @file = Puppet::Type::File.new(:name => @path) @catalog = Puppet::Resource::Catalog.new @file.catalog = @catalog end describe "when determining if recursion is enabled" do it "should default to recursion being disabled" do @file.should_not be_recurse end [true, "true", 10, "inf", "remote"].each do |value| it "should consider #{value} to enable recursion" do @file[:recurse] = value @file.must be_recurse end end [false, "false", 0].each do |value| it "should consider #{value} to disable recursion" do @file[:recurse] = value @file.should_not be_recurse end end end describe "#write" do it "should propagate failures encountered when renaming the temporary file" do File.stubs(:open) File.expects(:rename).raises ArgumentError file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") file.stubs(:validate_checksum?).returns(false) property = stub('content_property', :actual_content => "something", :length => "something".length) file.stubs(:property).with(:content).returns(property) lambda { file.write(:content) }.should raise_error(Puppet::Error) end it "should delegate writing to the content property" do filehandle = stub_everything 'fh' File.stubs(:open).yields(filehandle) File.stubs(:rename) property = stub('content_property', :actual_content => "something", :length => "something".length) file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") file.stubs(:validate_checksum?).returns(false) file.stubs(:property).with(:content).returns(property) property.expects(:write).with(filehandle) file.write(:content) end describe "when validating the checksum" do before { @file.stubs(:validate_checksum?).returns(true) } it "should fail if the checksum parameter and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b', :sum_file => 'checksum_b') @file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') @file.stubs(:property).with(:content).returns(property) lambda { @file.write :NOTUSED }.should raise_error(Puppet::Error) end end describe "when not validating the checksum" do before { @file.stubs(:validate_checksum?).returns(false) } it "should not fail if the checksum property and content checksums do not match" do checksum = stub('checksum_parameter', :sum => 'checksum_b') @file.stubs(:parameter).with(:checksum).returns(checksum) property = stub('content_property', :actual_content => "something", :length => "something".length, :write => 'checksum_a') @file.stubs(:property).with(:content).returns(property) lambda { @file.write :NOTUSED }.should_not raise_error(Puppet::Error) end end end it "should have a method for determining if the file is present" do @file.must respond_to(:exist?) end it "should be considered existent if it can be stat'ed" do @file.expects(:stat).returns mock('stat') @file.must be_exist end it "should be considered nonexistent if it can not be stat'ed" do @file.expects(:stat).returns nil @file.must_not be_exist end it "should have a method for determining if the file should be a normal file" do @file.must respond_to(:should_be_file?) end it "should be a file if :ensure is set to :file" do @file[:ensure] = :file @file.must be_should_be_file end it "should be a file if :ensure is set to :present and the file exists as a normal file" do @file.stubs(:stat).returns(mock('stat', :ftype => "file")) @file[:ensure] = :present @file.must be_should_be_file end it "should not be a file if :ensure is set to something other than :file" do @file[:ensure] = :directory @file.must_not be_should_be_file end it "should not be a file if :ensure is set to :present and the file exists but is not a normal file" do @file.stubs(:stat).returns(mock('stat', :ftype => "directory")) @file[:ensure] = :present @file.must_not be_should_be_file end it "should be a file if :ensure is not set and :content is" do @file[:content] = "foo" @file.must be_should_be_file end it "should be a file if neither :ensure nor :content is set but the file exists as a normal file" do @file.stubs(:stat).returns(mock("stat", :ftype => "file")) @file.must be_should_be_file end it "should not be a file if neither :ensure nor :content is set but the file exists but not as a normal file" do @file.stubs(:stat).returns(mock("stat", :ftype => "directory")) @file.must_not be_should_be_file end describe "when using POSIX filenames" do describe "on POSIX systems" do before do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should autorequire its parent directory" do file = Puppet::Type::File.new(:path => "/foo/bar") dir = Puppet::Type::File.new(:path => "/foo") @catalog.add_resource file @catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should autorequire its nearest ancestor directory" do file = Puppet::Type::File.new(:path => "/foo/bar/baz") dir = Puppet::Type::File.new(:path => "/foo") root = Puppet::Type::File.new(:path => "/") @catalog.add_resource file @catalog.add_resource dir @catalog.add_resource root reqs = file.autorequire reqs.length.must == 1 reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire anything when there is no nearest ancestor directory" do file = Puppet::Type::File.new(:path => "/foo/bar/baz") @catalog.add_resource file file.autorequire.should be_empty end it "should not autorequire its parent dir if its parent dir is itself" do file = Puppet::Type::File.new(:path => "/") @catalog.add_resource file file.autorequire.should be_empty end it "should remove trailing slashes" do file = Puppet::Type::File.new(:path => "/foo/bar/baz/") file[:path].should == "/foo/bar/baz" end it "should remove double slashes" do file = Puppet::Type::File.new(:path => "/foo/bar//baz") file[:path].should == "/foo/bar/baz" end it "should remove trailing double slashes" do file = Puppet::Type::File.new(:path => "/foo/bar/baz//") file[:path].should == "/foo/bar/baz" end it "should leave a single slash alone" do file = Puppet::Type::File.new(:path => "/") file[:path].should == "/" end it "should accept a double-slash at the start of the path" do expect { file = Puppet::Type::File.new(:path => "//tmp/xxx") # REVISIT: This should be wrong, later. See the next test. # --daniel 2011-01-31 file[:path].should == '/tmp/xxx' }.should_not raise_error end # REVISIT: This is pending, because I don't want to try and audit the # entire codebase to make sure we get this right. POSIX treats two (and # exactly two) '/' characters at the start of the path specially. # # See sections 3.2 and 4.11, which allow DomainOS to be all special like # and still have the POSIX branding and all. --daniel 2011-01-31 it "should preserve the double-slash at the start of the path" end describe "on Microsoft Windows systems" do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should refuse to work" do lambda { Puppet::Type::File.new(:path => "/foo/bar") }.should raise_error(Puppet::Error) end end end describe "when using Microsoft Windows filenames", :if => Puppet.features.microsoft_windows? do describe "on Microsoft Windows systems" do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should autorequire its parent directory" do file = Puppet::Type::File.new(:path => "X:/foo/bar") dir = Puppet::Type::File.new(:path => "X:/foo") @catalog.add_resource file @catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should autorequire its nearest ancestor directory" do file = Puppet::Type::File.new(:path => "X:/foo/bar/baz") dir = Puppet::Type::File.new(:path => "X:/foo") root = Puppet::Type::File.new(:path => "X:/") @catalog.add_resource file @catalog.add_resource dir @catalog.add_resource root reqs = file.autorequire reqs.length.must == 1 reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire anything when there is no nearest ancestor directory" do file = Puppet::Type::File.new(:path => "X:/foo/bar/baz") @catalog.add_resource file file.autorequire.should be_empty end it "should not autorequire its parent dir if its parent dir is itself" do file = Puppet::Type::File.new(:path => "X:/") @catalog.add_resource file file.autorequire.should be_empty end it "should remove trailing slashes" do file = Puppet::Type::File.new(:path => "X:/foo/bar/baz/") file[:path].should == "X:/foo/bar/baz" end it "should remove double slashes" do file = Puppet::Type::File.new(:path => "X:/foo/bar//baz") file[:path].should == "X:/foo/bar/baz" end it "should remove trailing double slashes" do file = Puppet::Type::File.new(:path => "X:/foo/bar/baz//") file[:path].should == "X:/foo/bar/baz" end it "should leave a drive letter with a slash alone", :'fails_on_ruby_1.9.2' => true do file = Puppet::Type::File.new(:path => "X:/") file[:path].should == "X:/" end - it "should add a slash to a drive letter", :'fails_on_ruby_1.9.2' => true do + it "should add a slash to a drive letter", :'fails_on_windows' => true, :'fails_on_ruby_1.9.2' => true do file = Puppet::Type::File.new(:path => "X:") file[:path].should == "X:/" end end describe "on POSIX systems" do before do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should refuse to work" do lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) end end end describe "when using UNC filenames" do describe "on Microsoft Windows systems", :if => Puppet.features.microsoft_windows?, :'fails_on_ruby_1.9.2' => true do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should autorequire its parent directory" do file = Puppet::Type::File.new(:path => "//server/foo/bar") dir = Puppet::Type::File.new(:path => "//server/foo") @catalog.add_resource file @catalog.add_resource dir reqs = file.autorequire reqs[0].source.must == dir reqs[0].target.must == file end it "should autorequire its nearest ancestor directory" do file = Puppet::Type::File.new(:path => "//server/foo/bar/baz/qux") dir = Puppet::Type::File.new(:path => "//server/foo/bar") root = Puppet::Type::File.new(:path => "//server/foo") @catalog.add_resource file @catalog.add_resource dir @catalog.add_resource root reqs = file.autorequire reqs.length.must == 1 reqs[0].source.must == dir reqs[0].target.must == file end it "should not autorequire anything when there is no nearest ancestor directory" do file = Puppet::Type::File.new(:path => "//server/foo/bar/baz/qux") @catalog.add_resource file file.autorequire.should be_empty end it "should not autorequire its parent dir if its parent dir is itself" do file = Puppet::Type::File.new(:path => "//server/foo") @catalog.add_resource file puts file.autorequire file.autorequire.should be_empty end it "should remove trailing slashes" do file = Puppet::Type::File.new(:path => "//server/foo/bar/baz/") file[:path].should == "//server/foo/bar/baz" end it "should remove double slashes" do file = Puppet::Type::File.new(:path => "//server/foo/bar//baz") file[:path].should == "//server/foo/bar/baz" end it "should remove trailing double slashes" do file = Puppet::Type::File.new(:path => "//server/foo/bar/baz//") file[:path].should == "//server/foo/bar/baz" end it "should remove a trailing slash from a sharename" do file = Puppet::Type::File.new(:path => "//server/foo/") file[:path].should == "//server/foo" end it "should not modify a sharename" do file = Puppet::Type::File.new(:path => "//server/foo") file[:path].should == "//server/foo" end end describe "on POSIX systems" do before do Puppet.features.stubs(:posix?).returns(true) Puppet.features.stubs(:microsoft_windows?).returns(false) end it "should refuse to work" do lambda { Puppet::Type::File.new(:path => "X:/foo/bar") }.should raise_error(Puppet::Error) end end end describe "when initializing" do it "should set a desired 'ensure' value if none is set and 'content' is set" do file = Puppet::Type::File.new(:name => "/my/file", :content => "/foo/bar") file[:ensure].should == :file end it "should set a desired 'ensure' value if none is set and 'target' is set" do file = Puppet::Type::File.new(:name => "/my/file", :target => "/foo/bar") file[:ensure].should == :symlink end end describe "when validating attributes" do %w{path checksum backup recurse recurselimit source replace force ignore links purge sourceselect}.each do |attr| it "should have a '#{attr}' parameter" do Puppet::Type.type(:file).attrtype(attr.intern).should == :param end end %w{content target ensure owner group mode type}.each do |attr| it "should have a '#{attr}' property" do Puppet::Type.type(:file).attrtype(attr.intern).should == :property end end it "should have its 'path' attribute set as its namevar" do Puppet::Type.type(:file).key_attributes.should == [:path] end end describe "when managing links" do require 'tempfile' if @real_posix describe "on POSIX systems" do before do @basedir = tempfile Dir.mkdir(@basedir) @file = File.join(@basedir, "file") @link = File.join(@basedir, "link") File.open(@file, "w", 0644) { |f| f.puts "yayness"; f.flush } File.symlink(@file, @link) @resource = Puppet::Type.type(:file).new(:path => @link, :mode => "755") @catalog.add_resource @resource end after do remove_tmp_files end it "should default to managing the link" do @catalog.apply # I convert them to strings so they display correctly if there's an error. ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0644 end it "should be able to follow links" do @resource[:links] = :follow @catalog.apply ("%o" % (File.stat(@file).mode & 007777)).should == "%o" % 0755 end end else # @real_posix # should recode tests using expectations instead of using the filesystem end describe "on Microsoft Windows systems" do before do Puppet.features.stubs(:posix?).returns(false) Puppet.features.stubs(:microsoft_windows?).returns(true) end it "should refuse to work with links" end end it "should be able to retrieve a stat instance for the file it is managing" do Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo").should respond_to(:stat) end describe "when stat'ing its file" do before do @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") @resource[:links] = :manage # so we always use :lstat end it "should use :stat if it is following links" do @resource[:links] = :follow File.expects(:stat) @resource.stat end it "should use :lstat if is it not following links" do @resource[:links] = :manage File.expects(:lstat) @resource.stat end it "should stat the path of the file" do File.expects(:lstat).with("/foo/bar") @resource.stat end # This only happens in testing. it "should return nil if the stat does not exist" do File.expects(:lstat).returns nil @resource.stat.should be_nil end it "should return nil if the file does not exist" do File.expects(:lstat).raises(Errno::ENOENT) @resource.stat.should be_nil end it "should return nil if the file cannot be stat'ed" do File.expects(:lstat).raises(Errno::EACCES) @resource.stat.should be_nil end it "should return the stat instance" do File.expects(:lstat).returns "mystat" @resource.stat.should == "mystat" end it "should cache the stat instance if it has a catalog and is applying" do stat = mock 'stat' File.expects(:lstat).returns stat catalog = Puppet::Resource::Catalog.new @resource.catalog = catalog catalog.stubs(:applying?).returns true @resource.stat.should equal(@resource.stat) end end describe "when flushing" do it "should flush all properties that respond to :flush" do @resource = Puppet::Type.type(:file).new(:path => "/foo/bar", :source => "/bar/foo") @resource.parameter(:source).expects(:flush) @resource.flush end it "should reset its stat reference" do @resource = Puppet::Type.type(:file).new(:path => "/foo/bar") File.expects(:lstat).times(2).returns("stat1").then.returns("stat2") @resource.stat.should == "stat1" @resource.flush @resource.stat.should == "stat2" end end it "should have a method for performing recursion" do @file.must respond_to(:perform_recursion) end describe "when executing a recursive search" do it "should use Metadata to do its recursion" do Puppet::FileServing::Metadata.indirection.expects(:search) @file.perform_recursion(@file[:path]) end it "should use the provided path as the key to the search" do Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| key == "/foo" } @file.perform_recursion("/foo") end it "should return the results of the metadata search" do Puppet::FileServing::Metadata.indirection.expects(:search).returns "foobar" @file.perform_recursion(@file[:path]).should == "foobar" end it "should pass its recursion value to the search" do @file[:recurse] = true Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } @file.perform_recursion(@file[:path]) end it "should pass true if recursion is remote" do @file[:recurse] = :remote Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurse] == true } @file.perform_recursion(@file[:path]) end it "should pass its recursion limit value to the search" do @file[:recurselimit] = 10 Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:recurselimit] == 10 } @file.perform_recursion(@file[:path]) end it "should configure the search to ignore or manage links" do @file[:links] = :manage Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:links] == :manage } @file.perform_recursion(@file[:path]) end it "should pass its 'ignore' setting to the search if it has one" do @file[:ignore] = %w{.svn CVS} Puppet::FileServing::Metadata.indirection.expects(:search).with { |key, options| options[:ignore] == %w{.svn CVS} } @file.perform_recursion(@file[:path]) end end it "should have a method for performing local recursion" do @file.must respond_to(:recurse_local) end describe "when doing local recursion" do before do @metadata = stub 'metadata', :relative_path => "my/file" end it "should pass its path to the :perform_recursion method" do @file.expects(:perform_recursion).with(@file[:path]).returns [@metadata] @file.stubs(:newchild) @file.recurse_local end it "should return an empty hash if the recursion returns nothing" do @file.expects(:perform_recursion).returns nil @file.recurse_local.should == {} end it "should create a new child resource with each generated metadata instance's relative path" do @file.expects(:perform_recursion).returns [@metadata] @file.expects(:newchild).with(@metadata.relative_path).returns "fiebar" @file.recurse_local end it "should not create a new child resource for the '.' directory" do @metadata.stubs(:relative_path).returns "." @file.expects(:perform_recursion).returns [@metadata] @file.expects(:newchild).never @file.recurse_local end it "should return a hash of the created resources with the relative paths as the hash keys" do @file.expects(:perform_recursion).returns [@metadata] @file.expects(:newchild).with("my/file").returns "fiebar" @file.recurse_local.should == {"my/file" => "fiebar"} end it "should set checksum_type to none if this file checksum is none" do @file[:checksum] = :none Puppet::FileServing::Metadata.indirection.expects(:search).with { |path,params| params[:checksum_type] == :none }.returns [@metadata] @file.expects(:newchild).with("my/file").returns "fiebar" @file.recurse_local end end it "should have a method for performing link recursion" do @file.must respond_to(:recurse_link) end describe "when doing link recursion" do before do @first = stub 'first', :relative_path => "first", :full_path => "/my/first", :ftype => "directory" @second = stub 'second', :relative_path => "second", :full_path => "/my/second", :ftype => "file" @resource = stub 'file', :[]= => nil end it "should pass its target to the :perform_recursion method" do @file[:target] = "mylinks" @file.expects(:perform_recursion).with("mylinks").returns [@first] @file.stubs(:newchild).returns @resource @file.recurse_link({}) end it "should ignore the recursively-found '.' file and configure the top-level file to create a directory" do @first.stubs(:relative_path).returns "." @file[:target] = "mylinks" @file.expects(:perform_recursion).with("mylinks").returns [@first] @file.stubs(:newchild).never @file.expects(:[]=).with(:ensure, :directory) @file.recurse_link({}) end it "should create a new child resource for each generated metadata instance's relative path that doesn't already exist in the children hash" do @file.expects(:perform_recursion).returns [@first, @second] @file.expects(:newchild).with(@first.relative_path).returns @resource @file.recurse_link("second" => @resource) end it "should not create a new child resource for paths that already exist in the children hash" do @file.expects(:perform_recursion).returns [@first] @file.expects(:newchild).never @file.recurse_link("first" => @resource) end it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory" do file = stub 'file' file.expects(:[]=).with(:target, "/my/second") file.expects(:[]=).with(:ensure, :link) @file.stubs(:perform_recursion).returns [@first, @second] @file.recurse_link("first" => @resource, "second" => file) end it "should :ensure to :directory if the file is a directory" do file = stub 'file' file.expects(:[]=).with(:ensure, :directory) @file.stubs(:perform_recursion).returns [@first, @second] @file.recurse_link("first" => file, "second" => @resource) end it "should return a hash with both created and existing resources with the relative paths as the hash keys" do file = stub 'file', :[]= => nil @file.expects(:perform_recursion).returns [@first, @second] @file.stubs(:newchild).returns file @file.recurse_link("second" => @resource).should == {"second" => @resource, "first" => file} end end it "should have a method for performing remote recursion" do @file.must respond_to(:recurse_remote) end describe "when doing remote recursion" do before do @file[:source] = "puppet://foo/bar" @first = Puppet::FileServing::Metadata.new("/my", :relative_path => "first") @second = Puppet::FileServing::Metadata.new("/my", :relative_path => "second") @first.stubs(:ftype).returns "directory" @second.stubs(:ftype).returns "directory" @parameter = stub 'property', :metadata= => nil @resource = stub 'file', :[]= => nil, :parameter => @parameter end it "should pass its source to the :perform_recursion method" do data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] @file.stubs(:newchild).returns @resource @file.recurse_remote({}) end it "should not recurse when the remote file is not a directory" do data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => ".") data.stubs(:ftype).returns "file" @file.expects(:perform_recursion).with("puppet://foo/bar").returns [data] @file.expects(:newchild).never @file.recurse_remote({}) end it "should set the source of each returned file to the searched-for URI plus the found relative path" do @first.expects(:source=).with File.join("puppet://foo/bar", @first.relative_path) @file.expects(:perform_recursion).returns [@first] @file.stubs(:newchild).returns @resource @file.recurse_remote({}) end it "should create a new resource for any relative file paths that do not already have a resource" do @file.stubs(:perform_recursion).returns [@first] @file.expects(:newchild).with("first").returns @resource @file.recurse_remote({}).should == {"first" => @resource} end it "should not create a new resource for any relative file paths that do already have a resource" do @file.stubs(:perform_recursion).returns [@first] @file.expects(:newchild).never @file.recurse_remote("first" => @resource) end it "should set the source of each resource to the source of the metadata" do @file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:source, File.join("puppet://foo/bar", @first.relative_path)) @file.recurse_remote("first" => @resource) end # LAK:FIXME This is a bug, but I can't think of a fix for it. Fortunately it's already # filed, and when it's fixed, we'll just fix the whole flow. it "should set the checksum type to :md5 if the remote file is a file" do @first.stubs(:ftype).returns "file" @file.stubs(:perform_recursion).returns [@first] @resource.stubs(:[]=) @resource.expects(:[]=).with(:checksum, :md5) @file.recurse_remote("first" => @resource) end it "should store the metadata in the source property for each resource so the source does not have to requery the metadata" do @file.stubs(:perform_recursion).returns [@first] @resource.expects(:parameter).with(:source).returns @parameter @parameter.expects(:metadata=).with(@first) @file.recurse_remote("first" => @resource) end it "should not create a new resource for the '.' file" do @first.stubs(:relative_path).returns "." @file.stubs(:perform_recursion).returns [@first] @file.expects(:newchild).never @file.recurse_remote({}) end it "should store the metadata in the main file's source property if the relative path is '.'" do @first.stubs(:relative_path).returns "." @file.stubs(:perform_recursion).returns [@first] @file.parameter(:source).expects(:metadata=).with @first @file.recurse_remote("first" => @resource) end describe "and multiple sources are provided" do describe "and :sourceselect is set to :first" do it "should create file instances for the results for the first source to return any values" do data = Puppet::FileServing::Metadata.new("/whatever", :relative_path => "foobar") @file[:source] = %w{/one /two /three /four} @file.expects(:perform_recursion).with("/one").returns nil @file.expects(:perform_recursion).with("/two").returns [] @file.expects(:perform_recursion).with("/three").returns [data] @file.expects(:perform_recursion).with("/four").never @file.expects(:newchild).with("foobar").returns @resource @file.recurse_remote({}) end end describe "and :sourceselect is set to :all" do before do @file[:sourceselect] = :all end it "should return every found file that is not in a previous source" do klass = Puppet::FileServing::Metadata @file[:source] = %w{/one /two /three /four} @file.stubs(:newchild).returns @resource one = [klass.new("/one", :relative_path => "a")] @file.expects(:perform_recursion).with("/one").returns one @file.expects(:newchild).with("a").returns @resource two = [klass.new("/two", :relative_path => "a"), klass.new("/two", :relative_path => "b")] @file.expects(:perform_recursion).with("/two").returns two @file.expects(:newchild).with("b").returns @resource three = [klass.new("/three", :relative_path => "a"), klass.new("/three", :relative_path => "c")] @file.expects(:perform_recursion).with("/three").returns three @file.expects(:newchild).with("c").returns @resource @file.expects(:perform_recursion).with("/four").returns [] @file.recurse_remote({}) end end end end describe "when specifying both source, and content properties" do before do @file[:source] = '/one' @file[:content] = 'file contents' end it "should raise an exception" do lambda {@file.validate }.should raise_error(/You cannot specify more than one of/) end end describe "when using source" do before do @file[:source] = '/one' end Puppet::Type::File::ParameterChecksum.value_collection.values.reject {|v| v == :none}.each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do @file[:checksum] = checksum_type end it 'should validate' do lambda { @file.validate }.should_not raise_error end end end describe "with checksum 'none'" do before do @file[:checksum] = :none end it 'should raise an exception when validating' do lambda { @file.validate }.should raise_error(/You cannot specify source when using checksum 'none'/) end end end describe "when using content" do before do @file[:content] = 'file contents' end (Puppet::Type::File::ParameterChecksum.value_collection.values - SOURCE_ONLY_CHECKSUMS).each do |checksum_type| describe "with checksum '#{checksum_type}'" do before do @file[:checksum] = checksum_type end it 'should validate' do lambda { @file.validate }.should_not raise_error end end end SOURCE_ONLY_CHECKSUMS.each do |checksum_type| describe "with checksum '#{checksum_type}'" do it 'should raise an exception when validating' do @file[:checksum] = checksum_type lambda { @file.validate }.should raise_error(/You cannot specify content when using checksum '#{checksum_type}'/) end end end end describe "when returning resources with :eval_generate" do before do @graph = stub 'graph', :add_edge => nil @catalog.stubs(:relationship_graph).returns @graph @file.catalog = @catalog @file[:recurse] = true end it "should recurse if recursion is enabled" do resource = stub('resource', :[] => "resource") @file.expects(:recurse?).returns true @file.expects(:recurse).returns [resource] @file.eval_generate.should == [resource] end it "should not recurse if recursion is disabled" do @file.expects(:recurse?).returns false @file.expects(:recurse).never @file.eval_generate.should == [] end it "should return each resource found through recursion" do foo = stub 'foo', :[] => "/foo" bar = stub 'bar', :[] => "/bar" bar2 = stub 'bar2', :[] => "/bar" @file.expects(:recurse).returns [foo, bar] @file.eval_generate.should == [foo, bar] end end describe "when recursing" do before do @file[:recurse] = true @metadata = Puppet::FileServing::Metadata end describe "and a source is set" do before { @file[:source] = "/my/source" } it "should pass the already-discovered resources to recurse_remote" do @file.stubs(:recurse_local).returns(:foo => "bar") @file.expects(:recurse_remote).with(:foo => "bar").returns [] @file.recurse end end describe "and a target is set" do before { @file[:target] = "/link/target" } it "should use recurse_link" do @file.stubs(:recurse_local).returns(:foo => "bar") @file.expects(:recurse_link).with(:foo => "bar").returns [] @file.recurse end end it "should use recurse_local if recurse is not remote" do @file.expects(:recurse_local).returns({}) @file.recurse end it "should not use recurse_local if recurse remote" do @file[:recurse] = :remote @file.expects(:recurse_local).never @file.recurse end it "should return the generated resources as an array sorted by file path" do one = stub 'one', :[] => "/one" two = stub 'two', :[] => "/one/two" three = stub 'three', :[] => "/three" @file.expects(:recurse_local).returns(:one => one, :two => two, :three => three) @file.recurse.should == [one, two, three] end describe "and purging is enabled" do before do @file[:purge] = true end it "should configure each file to be removed" do local = stub 'local' local.stubs(:[]).with(:source).returns nil # Thus, a local file local.stubs(:[]).with(:path).returns "foo" @file.expects(:recurse_local).returns("local" => local) local.expects(:[]=).with(:ensure, :absent) @file.recurse end it "should not remove files that exist in the remote repository" do @file["source"] = "/my/file" @file.expects(:recurse_local).returns({}) remote = stub 'remote' remote.stubs(:[]).with(:source).returns "/whatever" # Thus, a remote file remote.stubs(:[]).with(:path).returns "foo" @file.expects(:recurse_remote).with { |hash| hash["remote"] = remote } remote.expects(:[]=).with(:ensure, :absent).never @file.recurse end end describe "and making a new child resource" do it "should not copy the parent resource's parent" do Puppet::Type.type(:file).expects(:new).with { |options| ! options.include?(:parent) } @file.newchild("my/path") end {:recurse => true, :target => "/foo/bar", :ensure => :present, :alias => "yay", :source => "/foo/bar"}.each do |param, value| it "should not pass on #{param} to the sub resource" do @file = Puppet::Type::File.new(:name => @path, param => value, :catalog => @catalog) @file.class.expects(:new).with { |params| params[param].nil? } @file.newchild("sub/file") end end it "should copy all of the parent resource's 'should' values that were set at initialization" do file = @file.class.new(:path => "/foo/bar", :owner => "root", :group => "wheel") @catalog.add_resource(file) file.class.expects(:new).with { |options| options[:owner] == "root" and options[:group] == "wheel" } file.newchild("my/path") end it "should not copy default values to the new child" do @file.class.expects(:new).with { |params| params[:backup].nil? } @file.newchild("my/path") end it "should not copy values to the child which were set by the source" do @file[:source] = "/foo/bar" metadata = stub 'metadata', :owner => "root", :group => "root", :mode => 0755, :ftype => "file", :checksum => "{md5}whatever" @file.parameter(:source).stubs(:metadata).returns metadata @file.parameter(:source).copy_source_values @file.class.expects(:new).with { |params| params[:group].nil? } @file.newchild("my/path") end end end describe "when setting the backup" do it "should default to 'puppet'" do Puppet::Type::File.new(:name => "/my/file")[:backup].should == "puppet" end it "should allow setting backup to 'false'" do (!Puppet::Type::File.new(:name => "/my/file", :backup => false)[:backup]).should be_true end it "should set the backup to '.puppet-bak' if it is set to true" do Puppet::Type::File.new(:name => "/my/file", :backup => true)[:backup].should == ".puppet-bak" end it "should support any other backup extension" do Puppet::Type::File.new(:name => "/my/file", :backup => ".bak")[:backup].should == ".bak" end it "should set the filebucket when backup is set to a string matching the name of a filebucket in the catalog" do catalog = Puppet::Resource::Catalog.new bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file = Puppet::Type::File.new(:name => "/my/file") catalog.add_resource file file[:backup] = "foo" file.bucket.should == bucket_resource.bucket end it "should find filebuckets added to the catalog after the file resource was created" do catalog = Puppet::Resource::Catalog.new file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") catalog.add_resource file bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file.bucket.should == bucket_resource.bucket end it "should have a nil filebucket if backup is false" do catalog = Puppet::Resource::Catalog.new bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file = Puppet::Type::File.new(:name => "/my/file", :backup => false) catalog.add_resource file file.bucket.should be_nil end it "should have a nil filebucket if backup is set to a string starting with '.'" do catalog = Puppet::Resource::Catalog.new bucket_resource = Puppet::Type.type(:filebucket).new :name => "foo", :path => "/my/file/bucket" catalog.add_resource bucket_resource file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") catalog.add_resource file file.bucket.should be_nil end it "should fail if there's no catalog and backup is not false" do file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") lambda { file.bucket }.should raise_error(Puppet::Error) end it "should fail if a non-existent catalog is specified" do file = Puppet::Type::File.new(:name => "/my/file", :backup => "foo") catalog = Puppet::Resource::Catalog.new catalog.add_resource file lambda { file.bucket }.should raise_error(Puppet::Error) end it "should be able to use the default filebucket without a catalog" do file = Puppet::Type::File.new(:name => "/my/file", :backup => "puppet") file.bucket.should be_instance_of(Puppet::FileBucket::Dipper) end it "should look up the filebucket during finish()" do file = Puppet::Type::File.new(:name => "/my/file", :backup => ".foo") file.expects(:bucket) file.finish end end describe "when retrieving the current file state" do it "should copy the source values if the 'source' parameter is set" do file = Puppet::Type::File.new(:name => "/my/file", :source => "/foo/bar") file.parameter(:source).expects(:copy_source_values) file.retrieve end end describe ".title_patterns" do before do @type_class = Puppet::Type.type(:file) end it "should have a regexp that captures the entire string, except for a terminating slash" do patterns = @type_class.title_patterns string = "abc/\n\tdef/" patterns[0][0] =~ string $1.should == "abc/\n\tdef" end end describe "when auditing" do it "should not fail if creating a new file if group is not set" do File.exists?(@path).should == false file = Puppet::Type::File.new(:name => @path, :audit => "all", :content => "content") catalog = Puppet::Resource::Catalog.new catalog.add_resource(file) Puppet::Util::Storage.stubs(:store) # to prevent the catalog from trying to write state.yaml transaction = catalog.apply transaction.report.resource_statuses["File[#{@path}]"].failed.should == false File.exists?(@path).should == true end it "should not log errors if creating a new file with ensure present and no content" do File.exists?(@path).should == false file = Puppet::Type::File.new(:name => @path, :audit => "content", :ensure => "present") catalog = Puppet::Resource::Catalog.new catalog.add_resource(file) Puppet::Util::Storage.stubs(:store) # to prevent the catalog from trying to write state.yaml catalog.apply @logs.reject {|l| l.level == :notice }.should be_empty end end describe "when specifying both source and checksum" do it 'should use the specified checksum when source is first' do @file[:source] = '/foo' @file[:checksum] = :md5lite @file[:checksum].should be :md5lite end it 'should use the specified checksum when source is last' do @file[:checksum] = :md5lite @file[:source] = '/foo' @file[:checksum].should be :md5lite end end end diff --git a/spec/unit/type/group_spec.rb b/spec/unit/type/group_spec.rb index afe28247a..3b6cac8bc 100755 --- a/spec/unit/type/group_spec.rb +++ b/spec/unit/type/group_spec.rb @@ -1,63 +1,63 @@ #!/usr/bin/env rspec require 'spec_helper' -describe Puppet::Type.type(:group) do +describe Puppet::Type.type(:group), :fails_on_windows => true do before do ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") @class = Puppet::Type.type(:group) end it "should have a default provider" do @class.defaultprovider.should_not be_nil end it "should have a default provider inheriting from Puppet::Provider" do @class.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should have a system_groups feature" do @class.provider_feature(:system_groups).should_not be_nil end describe "when validating attributes" do [:name, :allowdupe].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:ensure, :gid].each do |param| it "should have a #{param} property" do @class.attrtype(param).should == :property end end it "should convert gids provided as strings into integers" do @class.new(:name => "foo", :gid => "15")[:gid].should == 15 end it "should accepts gids provided as integers" do @class.new(:name => "foo", :gid => 15)[:gid].should == 15 end end it "should have a boolean method for determining if duplicates are allowed", :'fails_on_ruby_1.9.2' => true do @class.new(:name => "foo").methods.should be_include("allowdupe?") end it "should have a boolean method for determining if system groups are allowed", :'fails_on_ruby_1.9.2' => true do @class.new(:name => "foo").methods.should be_include("system?") end it "should call 'create' to create the group" do group = @class.new(:name => "foo", :ensure => :present) group.provider.expects(:create) group.parameter(:ensure).sync end it "should call 'delete' to remove the group" do group = @class.new(:name => "foo", :ensure => :absent) group.provider.expects(:delete) group.parameter(:ensure).sync end end diff --git a/spec/unit/type/host_spec.rb b/spec/unit/type/host_spec.rb index 602c428af..145fb27ab 100755 --- a/spec/unit/type/host_spec.rb +++ b/spec/unit/type/host_spec.rb @@ -1,129 +1,129 @@ #!/usr/bin/env rspec require 'spec_helper' host = Puppet::Type.type(:host) describe host do before do @class = host @catalog = Puppet::Resource::Catalog.new @provider = stub 'provider' @resource = stub 'resource', :resource => nil, :provider => @provider end it "should have :name be its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider ].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:ip, :target, :host_aliases, :comment, :ensure].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end it "should have a list host_aliases" do @class.attrclass(:host_aliases).ancestors.should be_include(Puppet::Property::OrderedList) end end - describe "when validating values" do + describe "when validating values", :fails_on_windows => true do it "should support present as a value for ensure" do proc { @class.new(:name => "foo", :ensure => :present) }.should_not raise_error end it "should support absent as a value for ensure" do proc { @class.new(:name => "foo", :ensure => :absent) }.should_not raise_error end it "should accept IPv4 addresses" do proc { @class.new(:name => "foo", :ip => '10.96.0.1') }.should_not raise_error end it "should accept long IPv6 addresses" do # Taken from wikipedia article about ipv6 proc { @class.new(:name => "foo", :ip => '2001:0db8:85a3:08d3:1319:8a2e:0370:7344') }.should_not raise_error end it "should accept one host_alias" do proc { @class.new(:name => "foo", :host_aliases => 'alias1') }.should_not raise_error end it "should accept multiple host_aliases" do proc { @class.new(:name => "foo", :host_aliases => [ 'alias1', 'alias2' ]) }.should_not raise_error end it "should accept shortened IPv6 addresses" do proc { @class.new(:name => "foo", :ip => '2001:db8:0:8d3:0:8a2e:70:7344') }.should_not raise_error proc { @class.new(:name => "foo", :ip => '::ffff:192.0.2.128') }.should_not raise_error proc { @class.new(:name => "foo", :ip => '::1') }.should_not raise_error end it "should not accept malformed IPv4 addresses like 192.168.0.300" do proc { @class.new(:name => "foo", :ip => '192.168.0.300') }.should raise_error end it "should not accept malformed IP addresses like 2001:0dg8:85a3:08d3:1319:8a2e:0370:7344" do proc { @class.new(:name => "foo", :ip => '2001:0dg8:85a3:08d3:1319:8a2e:0370:7344') }.should raise_error end it "should not accept spaces in resourcename" do proc { @class.new(:name => "foo bar") }.should raise_error end it "should not accept host_aliases with spaces" do proc { @class.new(:name => "foo", :host_aliases => [ 'well_formed', 'not wellformed' ]) }.should raise_error end it "should not accept empty host_aliases" do proc { @class.new(:name => "foo", :host_aliases => ['alias1','']) }.should raise_error end end describe "when syncing" do it "should send the first value to the provider for ip property" do @ip = @class.attrclass(:ip).new(:resource => @resource, :should => %w{192.168.0.1 192.168.0.2}) @provider.expects(:ip=).with '192.168.0.1' @ip.sync end it "should send the first value to the provider for comment property" do @comment = @class.attrclass(:comment).new(:resource => @resource, :should => %w{Bazinga Notme}) @provider.expects(:comment=).with 'Bazinga' @comment.sync end it "should send the joined array to the provider for host_alias" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @provider.expects(:host_aliases=).with 'foo bar' @host_aliases.sync end it "should also use the specified delimiter for joining" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.stubs(:delimiter).returns "\t" @provider.expects(:host_aliases=).with "foo\tbar" @host_aliases.sync end it "should care about the order of host_aliases" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.insync?(%w{foo bar}).should == true @host_aliases.insync?(%w{bar foo}).should == false end it "should not consider aliases to be in sync if should is a subset of current" do @host_aliases = @class.attrclass(:host_aliases).new(:resource => @resource, :should => %w{foo bar}) @host_aliases.insync?(%w{foo bar anotherone}).should == false end end end diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb index 9ef76992a..3309cd267 100755 --- a/spec/unit/type/mount_spec.rb +++ b/spec/unit/type/mount_spec.rb @@ -1,329 +1,329 @@ #!/usr/bin/env rspec require 'spec_helper' -describe Puppet::Type.type(:mount) do +describe Puppet::Type.type(:mount), :fails_on_windows => true do it "should have a :refreshable feature that requires the :remount method" do Puppet::Type.type(:mount).provider_feature(:refreshable).methods.should == [:remount] end it "should have no default value for :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay") mount.should(:ensure).should be_nil end it "should have :name as the only keyattribut" do Puppet::Type.type(:mount).key_attributes.should == [:name] end end -describe Puppet::Type.type(:mount), "when validating attributes" do +describe Puppet::Type.type(:mount), "when validating attributes", :fails_on_windows => true do [:name, :remounts, :provider].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:mount).attrtype(param).should == :param end end [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| it "should have a #{param} property" do Puppet::Type.type(:mount).attrtype(param).should == :property end end end -describe Puppet::Type.type(:mount)::Ensure, "when validating values" do +describe Puppet::Type.type(:mount)::Ensure, "when validating values", :fails_on_windows => true do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil Puppet::Type.type(:mount).defaultprovider.expects(:new).returns(@provider) end it "should alias :present to :defined as a value to :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) mount.should(:ensure).should == :defined end it "should support :present as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) end it "should support :defined as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :defined) end it "should support :unmounted as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) end it "should support :absent as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :absent) end it "should support :mounted as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) end end -describe Puppet::Type.type(:mount)::Ensure do +describe Puppet::Type.type(:mount)::Ensure, :fails_on_windows => true do before :each do provider_properties = {} @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :property_hash => provider_properties Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :check => :ensure) @ensure = @mount.property(:ensure) end def mount_stub(params) Puppet::Type.type(:mount).validproperties.each do |prop| unless params[prop] params[prop] = :absent @mount[prop] = :absent end end params.each do |param, value| @provider.stubs(param).returns(value) end end describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do def test_ensure_change(options) @provider.stubs(:get).with(:ensure).returns options[:from] @provider.stubs(:ensure).returns options[:from] @provider.stubs(:mounted?).returns([:mounted,:ghost].include? options[:from]) @provider.expects(:create).times(options[:create] || 0) @provider.expects(:destroy).times(options[:destroy] || 0) @provider.expects(:mount).never @provider.expects(:unmount).times(options[:unmount] || 0) @ensure.stubs(:syncothers) @ensure.should = options[:to] @ensure.sync (!!@provider.property_hash[:needs_mount]).should == (!!options[:mount]) end it "should create itself when changing from :ghost to :present" do test_ensure_change(:from => :ghost, :to => :present, :create => 1) end it "should create itself when changing from :absent to :present" do test_ensure_change(:from => :absent, :to => :present, :create => 1) end it "should create itself and unmount when changing from :ghost to :unmounted" do test_ensure_change(:from => :ghost, :to => :unmounted, :create => 1, :unmount => 1) end it "should unmount resource when changing from :mounted to :unmounted" do test_ensure_change(:from => :mounted, :to => :unmounted, :unmount => 1) end it "should create itself when changing from :absent to :unmounted" do test_ensure_change(:from => :absent, :to => :unmounted, :create => 1) end it "should unmount resource when changing from :ghost to :absent" do test_ensure_change(:from => :ghost, :to => :absent, :unmount => 1) end it "should unmount and destroy itself when changing from :mounted to :absent" do test_ensure_change(:from => :mounted, :to => :absent, :destroy => 1, :unmount => 1) end it "should destroy itself when changing from :unmounted to :absent" do test_ensure_change(:from => :unmounted, :to => :absent, :destroy => 1) end it "should create itself when changing from :ghost to :mounted" do test_ensure_change(:from => :ghost, :to => :mounted, :create => 1) end it "should create itself and mount when changing from :absent to :mounted" do test_ensure_change(:from => :absent, :to => :mounted, :create => 1, :mount => 1) end it "should mount resource when changing from :unmounted to :mounted" do test_ensure_change(:from => :unmounted, :to => :mounted, :mount => 1) end it "should be in sync if it is :absent and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:absent).should == true end it "should be out of sync if it is :absent and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :absent and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :absent and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:absent).should == false end it "should be out of sync if it is :mounted and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:mounted).should == false end it "should be in sync if it is :mounted and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:mounted).should == true end it "should be in sync if it is :mounted and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:mounted).should == true end it "should be out in sync if it is :mounted and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:mounted).should == false end it "should be out of sync if it is :unmounted and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:unmounted).should == false end it "should be in sync if it is :unmounted and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:unmounted).should == true end it "should be out of sync if it is :unmounted and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:unmounted).should == false end it "should be in sync if it is :unmounted and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:unmounted).should == true end it "should be out of sync if it is :ghost and should be :absent" do @ensure.should = :absent @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :defined" do @ensure.should = :defined @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :mounted" do @ensure.should = :mounted @ensure.safe_insync?(:ghost).should == false end it "should be out of sync if it is :ghost and should be :unmounted" do @ensure.should = :unmounted @ensure.safe_insync?(:ghost).should == false end end describe Puppet::Type.type(:mount), "when responding to refresh" do pending "2.6.x specifies slightly different behavior and the desired behavior needs to be clarified and revisited. See ticket #4904" do it "should remount if it is supposed to be mounted" do @mount[:ensure] = "mounted" @provider.expects(:remount) @mount.refresh end it "should not remount if it is supposed to be present" do @mount[:ensure] = "present" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be absent" do @mount[:ensure] = "absent" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be defined" do @mount[:ensure] = "defined" @provider.expects(:remount).never @mount.refresh end it "should not remount if it is supposed to be unmounted" do @mount[:ensure] = "unmounted" @provider.expects(:remount).never @mount.refresh end it "should not remount swap filesystems" do @mount[:ensure] = "mounted" @mount[:fstype] = "swap" @provider.expects(:remount).never @mount.refresh end end end end -describe Puppet::Type.type(:mount), "when modifying an existing mount entry" do +describe Puppet::Type.type(:mount), "when modifying an existing mount entry", :fails_on_windows => true do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :remount => nil Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) {:device => "/foo/bar", :blockdevice => "/other/bar", :target => "/what/ever", :fstype => 'eh', :options => "", :pass => 0, :dump => 0, :atboot => 0, :ensure => :mounted}.each do |param, value| @mount.provider.stubs(param).returns value @mount[param] = value end @mount.provider.stubs(:mounted?).returns true # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @mount end it "should use the provider to change the dump value" do @mount.provider.expects(:dump).returns 0 @mount.provider.expects(:dump=).with(1) @mount[:dump] = 1 @catalog.apply end it "should umount before flushing changes to disk" do syncorder = sequence('syncorder') @mount.provider.expects(:options).returns 'soft' @mount.provider.expects(:ensure).returns :mounted @mount.provider.expects(:unmount).in_sequence(syncorder) @mount.provider.expects(:options=).in_sequence(syncorder).with 'hard' @mount.expects(:flush).in_sequence(syncorder) # Call inside syncothers @mount.expects(:flush).in_sequence(syncorder) # I guess transaction or anything calls flush again @mount[:ensure] = :unmounted @mount[:options] = 'hard' @catalog.apply end end diff --git a/spec/unit/type/noop_metaparam_spec.rb b/spec/unit/type/noop_metaparam_spec.rb index f4241d417..7083dd037 100755 --- a/spec/unit/type/noop_metaparam_spec.rb +++ b/spec/unit/type/noop_metaparam_spec.rb @@ -1,36 +1,38 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/type' describe Puppet::Type.type(:file).attrclass(:noop) do + include PuppetSpec::Files + before do Puppet.settings.stubs(:use) - @file = Puppet::Type.newfile :path => "/what/ever" + @file = Puppet::Type.newfile :path => make_absolute("/what/ever") end it "should accept true as a value" do lambda { @file[:noop] = true }.should_not raise_error end it "should accept false as a value" do lambda { @file[:noop] = false }.should_not raise_error end describe "when set on a resource" do it "should default to the :noop setting" do Puppet.settings.expects(:value).with(:noop).returns "myval" @file.noop.should == "myval" end it "should prefer true values from the attribute" do @file[:noop] = true @file.noop.should be_true end it "should prefer false values from the attribute" do @file[:noop] = false @file.noop.should be_false end end end diff --git a/spec/unit/type/resources_spec.rb b/spec/unit/type/resources_spec.rb index 48c068cfa..5e9396b24 100755 --- a/spec/unit/type/resources_spec.rb +++ b/spec/unit/type/resources_spec.rb @@ -1,101 +1,101 @@ #!/usr/bin/env rspec require 'spec_helper' resources = Puppet::Type.type(:resources) # There are still plenty of tests to port over from test/. describe resources do describe "when initializing" do it "should fail if the specified resource type does not exist" do Puppet::Type.stubs(:type).with { |x| x.to_s.downcase == "resources"}.returns resources Puppet::Type.expects(:type).with("nosuchtype").returns nil lambda { resources.new :name => "nosuchtype" }.should raise_error(Puppet::Error) end it "should not fail when the specified resource type exists" do lambda { resources.new :name => "file" }.should_not raise_error end it "should set its :resource_type attribute" do resources.new(:name => "file").resource_type.should == Puppet::Type.type(:file) end end - describe "#generate" do + describe "#generate", :fails_on_windows => true do before do @host1 = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') @catalog = Puppet::Resource::Catalog.new @context = Puppet::Transaction.new(@catalog) end describe "when dealing with non-purging resources" do before do @resources = Puppet::Type.type(:resources).new(:name => 'host') end it "should not generate any resource" do @resources.generate.should be_empty end end describe "when the catalog contains a purging resource" do before do @resources = Puppet::Type.type(:resources).new(:name => 'host', :purge => true) @purgeable_resource = Puppet::Type.type(:host).new(:name => 'localhost', :ip => '127.0.0.1') @catalog.add_resource @resources end it "should not generate a duplicate of that resource" do Puppet::Type.type(:host).stubs(:instances).returns [@host1] @catalog.add_resource @host1 @resources.generate.collect { |r| r.ref }.should_not include(@host1.ref) end it "should not include the skipped users", :'fails_on_ruby_1.9.2' => true do res = Puppet::Type.type(:resources).new :name => :user, :purge => true res.catalog = Puppet::Resource::Catalog.new users = [ Puppet::Type.type(:user).new(:name => "root") ] Puppet::Type.type(:user).expects(:instances).returns users list = res.generate names = list.collect { |r| r[:name] } names.should_not be_include("root") end describe "when generating a purgeable resource" do it "should be included in the generated resources" do Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] @resources.generate.collect { |r| r.ref }.should include(@purgeable_resource.ref) end end describe "when the instance's do not have an ensure property" do it "should not be included in the generated resources" do @no_ensure_resource = Puppet::Type.type(:exec).new(:name => '/usr/bin/env echo') Puppet::Type.type(:host).stubs(:instances).returns [@no_ensure_resource] @resources.generate.collect { |r| r.ref }.should_not include(@no_ensure_resource.ref) end end describe "when the instance's ensure property does not accept absent" do it "should not be included in the generated resources" do @no_absent_resource = Puppet::Type.type(:service).new(:name => 'foobar') Puppet::Type.type(:host).stubs(:instances).returns [@no_absent_resource] @resources.generate.collect { |r| r.ref }.should_not include(@no_absent_resource.ref) end end describe "when checking the instance fails" do it "should not be included in the generated resources" do @purgeable_resource = Puppet::Type.type(:host).new(:name => 'foobar') Puppet::Type.type(:host).stubs(:instances).returns [@purgeable_resource] @resources.expects(:check).with(@purgeable_resource).returns(false) @resources.generate.collect { |r| r.ref }.should_not include(@purgeable_resource.ref) end end end end end diff --git a/spec/unit/type/ssh_authorized_key_spec.rb b/spec/unit/type/ssh_authorized_key_spec.rb index 71b8a9ab0..9b3760b71 100755 --- a/spec/unit/type/ssh_authorized_key_spec.rb +++ b/spec/unit/type/ssh_authorized_key_spec.rb @@ -1,261 +1,263 @@ #!/usr/bin/env rspec require 'spec_helper' ssh_authorized_key = Puppet::Type.type(:ssh_authorized_key) describe ssh_authorized_key do + include PuppetSpec::Files + before do @class = Puppet::Type.type(:ssh_authorized_key) @provider_class = stub 'provider_class', :name => "fake", :suitable? => true, :supports_parameter? => true @class.stubs(:defaultprovider).returns(@provider_class) @class.stubs(:provider).returns(@provider_class) - @provider = stub 'provider', :class => @provider_class, :file_path => "/tmp/whatever", :clear => nil + @provider = stub 'provider', :class => @provider_class, :file_path => make_absolute("/tmp/whatever"), :clear => nil @provider_class.stubs(:new).returns(@provider) @catalog = Puppet::Resource::Catalog.new end it "should have :name be its namevar" do @class.key_attributes.should == [:name] end describe "when validating attributes" do [:name, :provider].each do |param| it "should have a #{param} parameter" do @class.attrtype(param).should == :param end end [:type, :key, :user, :target, :options, :ensure].each do |property| it "should have a #{property} property" do @class.attrtype(property).should == :property end end end describe "when validating values" do describe "for name" do it "should support valid names" do proc { @class.new(:name => "username", :ensure => :present, :user => "nobody") }.should_not raise_error proc { @class.new(:name => "username@hostname", :ensure => :present, :user => "nobody") }.should_not raise_error end it "should not support whitespaces" do proc { @class.new(:name => "my test", :ensure => :present, :user => "nobody") }.should raise_error(Puppet::Error,/Resourcename must not contain whitespace/) proc { @class.new(:name => "my\ttest", :ensure => :present, :user => "nobody") }.should raise_error(Puppet::Error,/Resourcename must not contain whitespace/) end end describe "for ensure" do it "should support :present" do proc { @class.new(:name => "whev", :ensure => :present, :user => "nobody") }.should_not raise_error end it "should support :absent" do proc { @class.new(:name => "whev", :ensure => :absent, :user => "nobody") }.should_not raise_error end it "should not support other values" do proc { @class.new(:name => "whev", :ensure => :foo, :user => "nobody") }.should raise_error(Puppet::Error, /Invalid value/) end end describe "for type" do it "should support ssh-dss" do proc { @class.new(:name => "whev", :type => "ssh-dss", :user => "nobody") }.should_not raise_error end it "should support ssh-rsa" do proc { @class.new(:name => "whev", :type => "ssh-rsa", :user => "nobody") }.should_not raise_error end it "should support :dsa" do proc { @class.new(:name => "whev", :type => :dsa, :user => "nobody") }.should_not raise_error end it "should support :rsa" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody") }.should_not raise_error end it "should alias :rsa to :ssh-rsa" do key = @class.new(:name => "whev", :type => :rsa, :user => "nobody") key.should(:type).should == :'ssh-rsa' end it "should alias :dsa to :ssh-dss" do key = @class.new(:name => "whev", :type => :dsa, :user => "nobody") key.should(:type).should == :'ssh-dss' end it "should not support values other than ssh-dss, ssh-rsa, dsa, rsa" do proc { @class.new(:name => "whev", :type => :something) }.should raise_error(Puppet::Error,/Invalid value/) end end describe "for key" do it "should support a valid key like a 1024 bit rsa key" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDCPfzW2ry7XvMc6E5Kj2e5fF/YofhKEvsNMUogR3PGL/HCIcBlsEjKisrY0aYgD8Ikp7ZidpXLbz5dBsmPy8hJiBWs5px9ZQrB/EOQAwXljvj69EyhEoGawmxQMtYw+OAIKHLJYRuk1QiHAMHLp5piqem8ZCV2mLb9AsJ6f7zUVw==')}.should_not raise_error end it "should support a valid key like a 4096 bit rsa key" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAAAB3NzaC1yc2EAAAADAQABAAACAQDEY4pZFyzSfRc9wVWI3DfkgT/EL033UZm/7x1M+d+lBD00qcpkZ6CPT7lD3Z+vylQlJ5S8Wcw6C5Smt6okZWY2WXA9RCjNJMIHQbJAzwuQwgnwU/1VMy9YPp0tNVslg0sUUgpXb13WW4mYhwxyGmIVLJnUrjrQmIFhtfHsJAH8ZVqCWaxKgzUoC/YIu1u1ScH93lEdoBPLlwm6J0aiM7KWXRb7Oq1nEDZtug1zpX5lhgkQWrs0BwceqpUbY+n9sqeHU5e7DCyX/yEIzoPRW2fe2Gx1Iq6JKM/5NNlFfaW8rGxh3Z3S1NpzPHTRjw8js3IeGiV+OPFoaTtM1LsWgPDSBlzIdyTbSQR7gKh0qWYCNV/7qILEfa0yIFB5wIo4667iSPZw2pNgESVtenm8uXyoJdk8iWQ4mecdoposV/znknNb2GPgH+n/2vme4btZ0Sl1A6rev22GQjVgbWOn8zaDglJ2vgCN1UAwmq41RXprPxENGeLnWQppTnibhsngu0VFllZR5kvSIMlekLRSOFLFt92vfd+tk9hZIiKm9exxcbVCGGQPsf6dZ27rTOmg0xM2Sm4J6RRKuz79HQgA4Eg18+bqRP7j/itb89DmtXEtoZFAsEJw8IgIfeGGDtHTkfAlAC92mtK8byeaxGq57XCTKbO/r5gcOMElZHy1AcB8kw==')}.should_not raise_error end it "should support a valid key like a 1024 bit dsa key" do proc { @class.new(:name => "whev", :type => :dsa, :user => "nobody", :key => 'AAAAB3NzaC1kc3MAAACBAI80iR78QCgpO4WabVqHHdEDigOjUEHwIjYHIubR/7u7DYrXY+e+TUmZ0CVGkiwB/0yLHK5dix3Y/bpj8ZiWCIhFeunnXccOdE4rq5sT2V3l1p6WP33RpyVYbLmeuHHl5VQ1CecMlca24nHhKpfh6TO/FIwkMjghHBfJIhXK+0w/AAAAFQDYzLupuMY5uz+GVrcP+Kgd8YqMmwAAAIB3SVN71whLWjFPNTqGyyIlMy50624UfNOaH4REwO+Of3wm/cE6eP8n75vzTwQGBpJX3BPaBGW1S1Zp/DpTOxhCSAwZzAwyf4WgW7YyAOdxN3EwTDJZeyiyjWMAOjW9/AOWt9gtKg0kqaylbMHD4kfiIhBzo31ZY81twUzAfN7angAAAIBfva8sTSDUGKsWWIXkdbVdvM4X14K4gFdy0ZJVzaVOtZ6alysW6UQypnsl6jfnbKvsZ0tFgvcX/CPyqNY/gMR9lyh/TCZ4XQcbqeqYPuceGehz+jL5vArfqsW2fJYFzgCcklmr/VxtP5h6J/T0c9YcDgc/xIfWdZAlznOnphI/FA==')}.should_not raise_error end it "should not support whitespaces" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :key => 'AAA FA==')}.should raise_error(Puppet::Error,/Key must not contain whitespace/) end end describe "for options" do it "should support flags as options" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority')}.should_not raise_error proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'no-port-forwarding')}.should_not raise_error end it "should support key-value pairs as options" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'command="command"')}.should_not raise_error end it "should support environments as options" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'environment="NAME=value"')}.should_not raise_error end it "should support multiple options as an array" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ['cert-authority','environment="NAME=value"'])}.should_not raise_error end it "should not support a comma separated lists" do proc { @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => 'cert-authority,no-port-forwarding')}.should raise_error(Puppet::Error, /must be provided as an array/) end it "should use :absent as a default value" do @class.new(:name => "whev", :type => :rsa, :user => "nobody").should(:options).should == [:absent] end it "property should return well formed string of arrays from is_to_s" do resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) resource.property(:options).is_to_s(["a","b","c"]).should == "a,b,c" end it "property should return well formed string of arrays from is_to_s" do resource = @class.new(:name => "whev", :type => :rsa, :user => "nobody", :options => ["a","b","c"]) resource.property(:options).should_to_s(["a","b","c"]).should == "a,b,c" end end describe "for user" do it "should support present users" do proc { @class.new(:name => "whev", :type => :rsa, :user => "root") }.should_not raise_error end it "should support absent users" do proc { @class.new(:name => "whev", :type => :rsa, :user => "ihopeimabsent") }.should_not raise_error end end describe "for target" do it "should support absolute paths" do proc { @class.new(:name => "whev", :type => :rsa, :target => "/tmp/here") }.should_not raise_error end - it "should use the user's path if not explicitly specified" do + it "should use the user's path if not explicitly specified", :fails_on_windows => true do @class.new(:name => "whev", :user => 'root').should(:target).should == File.expand_path("~root/.ssh/authorized_keys") end it "should not consider the user's path if explicitly specified" do @class.new(:name => "whev", :user => 'root', :target => '/tmp/here').should(:target).should == '/tmp/here' end it "should inform about an absent user" do Puppet::Log.level = :debug @class.new(:name => "whev", :user => 'idontexist').should(:target) @logs.map(&:message).should include("The required user is not yet present on the system") end end end describe "when neither user nor target is specified" do it "should raise an error" do proc do @class.new( :name => "Test", :key => "AAA", :type => "ssh-rsa", :ensure => :present) end.should raise_error(Puppet::Error,/user.*or.*target.*mandatory/) end end describe "when both target and user are specified" do it "should use target" do resource = @class.new( :name => "Test", :user => "root", :target => "/tmp/blah" ) resource.should(:target).should == "/tmp/blah" end end - describe "when user is specified" do + describe "when user is specified", :fails_on_windows => true do it "should determine target" do resource = @class.create( :name => "Test", :user => "root" ) target = File.expand_path("~root/.ssh/authorized_keys") resource.should(:target).should == target end # Bug #2124 - ssh_authorized_key always changes target if target is not defined it "should not raise spurious change events" do resource = @class.new(:name => "Test", :user => "root") target = File.expand_path("~root/.ssh/authorized_keys") resource.property(:target).safe_insync?(target).should == true end end describe "when calling validate" do it "should not crash on a non-existant user" do resource = @class.create( :name => "Test", :user => "ihopesuchuserdoesnotexist" ) proc { resource.validate }.should_not raise_error end end end diff --git a/spec/unit/type/tidy_spec.rb b/spec/unit/type/tidy_spec.rb index cb030634b..bf892e836 100755 --- a/spec/unit/type/tidy_spec.rb +++ b/spec/unit/type/tidy_spec.rb @@ -1,424 +1,426 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/file_bucket/dipper' tidy = Puppet::Type.type(:tidy) describe tidy do + include PuppetSpec::Files + before do - @basepath = Puppet.features.posix? ? "/what/ever" : "C:/tmp" + @basepath = make_absolute("/what/ever") Puppet.settings.stubs(:use) # for an unknown reason some of these specs fails when run individually # with a failed expectation on File.lstat in the autoloader. File.stubs(:lstat) end it "should use :lstat when stating a file" do resource = tidy.new :path => "/foo/bar", :age => "1d" stat = mock 'stat' File.expects(:lstat).with("/foo/bar").returns stat resource.stat("/foo/bar").should == stat end [:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:tidy).attrclass(param).ancestors.should be_include(Puppet::Parameter) end it "should have documentation for its #{param} param" do Puppet::Type.type(:tidy).attrclass(param).doc.should be_instance_of(String) end end describe "when validating parameter values" do describe "for 'recurse'" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" end it "should allow 'true'" do lambda { @tidy[:recurse] = true }.should_not raise_error end it "should allow 'false'" do lambda { @tidy[:recurse] = false }.should_not raise_error end it "should allow integers" do lambda { @tidy[:recurse] = 10 }.should_not raise_error end it "should allow string representations of integers" do lambda { @tidy[:recurse] = "10" }.should_not raise_error end it "should allow 'inf'" do lambda { @tidy[:recurse] = "inf" }.should_not raise_error end it "should not allow arbitrary values" do lambda { @tidy[:recurse] = "whatever" }.should raise_error end end describe "for 'matches'" do before do @tidy = Puppet::Type.type(:tidy).new :path => "/tmp", :age => "100d" end it "should object if matches is given with recurse is not specified" do lambda { @tidy[:matches] = '*.doh' }.should raise_error end it "should object if matches is given and recurse is 0" do lambda { @tidy[:recurse] = 0; @tidy[:matches] = '*.doh' }.should raise_error end it "should object if matches is given and recurse is false" do lambda { @tidy[:recurse] = false; @tidy[:matches] = '*.doh' }.should raise_error end it "should not object if matches is given and recurse is > 0" do lambda { @tidy[:recurse] = 1; @tidy[:matches] = '*.doh' }.should_not raise_error end it "should not object if matches is given and recurse is true" do lambda { @tidy[:recurse] = true; @tidy[:matches] = '*.doh' }.should_not raise_error end end end describe "when matching files by age" do convertors = { :second => 1, :minute => 60 } convertors[:hour] = convertors[:minute] * 60 convertors[:day] = convertors[:hour] * 24 convertors[:week] = convertors[:day] * 7 convertors.each do |unit, multiple| it "should consider a #{unit} to be #{multiple} seconds" do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :age => "5#{unit.to_s[0..0]}" @tidy[:age].should == 5 * multiple end end end describe "when matching files by size" do convertors = { :b => 0, :kb => 1, :mb => 2, :gb => 3, :tb => 4 } convertors.each do |unit, multiple| it "should consider a #{unit} to be 1024^#{multiple} bytes" do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :size => "5#{unit}" total = 5 multiple.times { total *= 1024 } @tidy[:size].should == total end end end describe "when tidying" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "directory" File.stubs(:lstat).with(@basepath).returns @stat end describe "and generating files" do it "should set the backup on the file if backup is set on the tidy instance" do @tidy[:backup] = "whatever" Puppet::Type.type(:file).expects(:new).with { |args| args[:backup] == "whatever" } @tidy.mkfile(@basepath) end it "should set the file's path to the tidy's path" do Puppet::Type.type(:file).expects(:new).with { |args| args[:path] == @basepath } @tidy.mkfile(@basepath) end it "should configure the file for deletion" do Puppet::Type.type(:file).expects(:new).with { |args| args[:ensure] == :absent } @tidy.mkfile(@basepath) end it "should force deletion on the file" do Puppet::Type.type(:file).expects(:new).with { |args| args[:force] == true } @tidy.mkfile(@basepath) end it "should do nothing if the targeted file does not exist" do File.expects(:lstat).with(@basepath).raises Errno::ENOENT @tidy.generate.should == [] end end describe "and recursion is not used" do it "should generate a file resource if the file should be tidied" do @tidy.expects(:tidy?).with(@basepath).returns true file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") @tidy.expects(:mkfile).with(@basepath).returns file @tidy.generate.should == [file] end it "should do nothing if the file should not be tidied" do @tidy.expects(:tidy?).with(@basepath).returns false @tidy.expects(:mkfile).never @tidy.generate.should == [] end end describe "and recursion is used" do before do @tidy[:recurse] = true Puppet::FileServing::Fileset.any_instance.stubs(:stat).returns mock("stat") @fileset = Puppet::FileServing::Fileset.new(@basepath) Puppet::FileServing::Fileset.stubs(:new).returns @fileset end it "should use a Fileset for infinite recursion" do Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns @fileset @fileset.expects(:files).returns %w{. one two} @tidy.stubs(:tidy?).returns false @tidy.generate end it "should use a Fileset for limited recursion" do @tidy[:recurse] = 42 Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true, :recurselimit => 42).returns @fileset @fileset.expects(:files).returns %w{. one two} @tidy.stubs(:tidy?).returns false @tidy.generate end it "should generate a file resource for every file that should be tidied but not for files that should not be tidied" do @fileset.expects(:files).returns %w{. one two} @tidy.expects(:tidy?).with(@basepath).returns true @tidy.expects(:tidy?).with(@basepath+"/one").returns true @tidy.expects(:tidy?).with(@basepath+"/two").returns false file = Puppet::Type.type(:file).new(:path => @basepath+"/eh") @tidy.expects(:mkfile).with(@basepath).returns file @tidy.expects(:mkfile).with(@basepath+"/one").returns file @tidy.generate end end describe "and determining whether a file matches provided glob patterns" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath, :recurse => 1 @tidy[:matches] = %w{*foo* *bar*} @stat = mock 'stat' @matcher = @tidy.parameter(:matches) end it "should always convert the globs to an array" do @matcher.value = "*foo*" @matcher.value.should == %w{*foo*} end it "should return true if any pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.must be_tidy("/file/yaybarness", @stat) end it "should return false if no pattern matches the last part of the file" do @matcher.value = %w{*foo* *bar*} @matcher.should_not be_tidy("/file/yayness", @stat) end end describe "and determining whether a file is too old" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat' @tidy[:age] = "1s" @tidy[:type] = "mtime" @ager = @tidy.parameter(:age) end it "should use the age type specified" do @tidy[:type] = :ctime @stat.expects(:ctime).returns(Time.now) @ager.tidy?(@basepath, @stat) end it "should return false if the file is more recent than the specified age" do @stat.expects(:mtime).returns(Time.now) @ager.should_not be_tidy(@basepath, @stat) end it "should return true if the file is older than the specified age" do @stat.expects(:mtime).returns(Time.now - 10) @ager.must be_tidy(@basepath, @stat) end end describe "and determining whether a file is too large" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "file" @tidy[:size] = "1kb" @sizer = @tidy.parameter(:size) end it "should return false if the file is smaller than the specified size" do @stat.expects(:size).returns(4) # smaller than a kilobyte @sizer.should_not be_tidy(@basepath, @stat) end it "should return true if the file is larger than the specified size" do @stat.expects(:size).returns(1500) # larger than a kilobyte @sizer.must be_tidy(@basepath, @stat) end it "should return true if the file is equal to the specified size" do @stat.expects(:size).returns(1024) @sizer.must be_tidy(@basepath, @stat) end end describe "and determining whether a file should be tidied" do before do @tidy = Puppet::Type.type(:tidy).new :path => @basepath @stat = stub 'stat', :ftype => "file" File.stubs(:lstat).with(@basepath).returns @stat end it "should not try to recurse if the file does not exist" do @tidy[:recurse] = true File.stubs(:lstat).with(@basepath).returns nil @tidy.generate.should == [] end it "should not be tidied if the file does not exist" do File.expects(:lstat).with(@basepath).raises Errno::ENOENT @tidy.should_not be_tidy(@basepath) end it "should not be tidied if the user has no access to the file" do File.expects(:lstat).with(@basepath).raises Errno::EACCES @tidy.should_not be_tidy(@basepath) end it "should not be tidied if it is a directory and rmdirs is set to false" do stat = mock 'stat', :ftype => "directory" File.expects(:lstat).with(@basepath).returns stat @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match any provided globs" do @tidy[:recurse] = 1 @tidy[:matches] = "globs" matches = @tidy.parameter(:matches) matches.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match aging requirements" do @tidy[:age] = "1d" ager = @tidy.parameter(:age) ager.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should return false if it does not match size requirements" do @tidy[:size] = "1b" sizer = @tidy.parameter(:size) sizer.expects(:tidy?).with(@basepath, @stat).returns false @tidy.should_not be_tidy(@basepath) end it "should tidy a file if age and size are set but only size matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns true @tidy.parameter(:age).stubs(:tidy?).returns false @tidy.should be_tidy(@basepath) end it "should tidy a file if age and size are set but only age matches" do @tidy[:size] = "1b" @tidy[:age] = "1d" @tidy.parameter(:size).stubs(:tidy?).returns false @tidy.parameter(:age).stubs(:tidy?).returns true @tidy.should be_tidy(@basepath) end it "should tidy all files if neither age nor size is set" do @tidy.must be_tidy(@basepath) end it "should sort the results inversely by path length, so files are added to the catalog before their directories" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = Puppet::FileServing::Fileset.new(@basepath) Puppet::FileServing::Fileset.expects(:new).returns fileset fileset.expects(:files).returns %w{. one one/two} @tidy.stubs(:tidy?).returns true @tidy.generate.collect { |r| r[:path] }.should == [@basepath+"/one/two", @basepath+"/one", @basepath] end end it "should configure directories to require their contained files if rmdirs is enabled, so the files will be deleted first" do @tidy[:recurse] = true @tidy[:rmdirs] = true fileset = mock 'fileset' Puppet::FileServing::Fileset.expects(:new).with(@basepath, :recurse => true).returns fileset fileset.expects(:files).returns %w{. one two one/subone two/subtwo one/subone/ssone} @tidy.stubs(:tidy?).returns true result = @tidy.generate.inject({}) { |hash, res| hash[res[:path]] = res; hash } { @basepath => [ @basepath+"/one", @basepath+"/two" ], @basepath+"/one" => [@basepath+"/one/subone"], @basepath+"/two" => [@basepath+"/two/subtwo"], @basepath+"/one/subone" => [@basepath+"/one/subone/ssone"] }.each do |parent, children| children.each do |child| ref = Puppet::Resource.new(:file, child) result[parent][:require].find { |req| req.to_s == ref.to_s }.should_not be_nil end end end end end diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb index 71c9e1857..2da755ef2 100755 --- a/spec/unit/type/user_spec.rb +++ b/spec/unit/type/user_spec.rb @@ -1,335 +1,335 @@ #!/usr/bin/env rspec require 'spec_helper' user = Puppet::Type.type(:user) -describe user do +describe user, :fails_on_windows => true do before do ENV["PATH"] += File::PATH_SEPARATOR + "/usr/sbin" unless ENV["PATH"].split(File::PATH_SEPARATOR).include?("/usr/sbin") @provider = stub 'provider' @resource = stub 'resource', :resource => nil, :provider => @provider, :line => nil, :file => nil end it "should have a default provider inheriting from Puppet::Provider" do user.defaultprovider.ancestors.should be_include(Puppet::Provider) end it "should be able to create a instance" do user.new(:name => "foo").should_not be_nil end it "should have an allows_duplicates feature" do user.provider_feature(:allows_duplicates).should_not be_nil end it "should have an manages_homedir feature" do user.provider_feature(:manages_homedir).should_not be_nil end it "should have an manages_passwords feature" do user.provider_feature(:manages_passwords).should_not be_nil end it "should have a manages_solaris_rbac feature" do user.provider_feature(:manages_solaris_rbac).should_not be_nil end it "should have a manages_expiry feature" do user.provider_feature(:manages_expiry).should_not be_nil end it "should have a manages_password_age feature" do user.provider_feature(:manages_password_age).should_not be_nil end it "should have a system_users feature" do user.provider_feature(:system_users).should_not be_nil end describe "instances" do it "should have a valid provider" do user.new(:name => "foo").provider.class.ancestors.should be_include(Puppet::Provider) end it "should delegate existence questions to its provider" do instance = user.new(:name => "foo") instance.provider.expects(:exists?).returns "eh" instance.exists?.should == "eh" end end properties = [:ensure, :uid, :gid, :home, :comment, :shell, :password, :password_min_age, :password_max_age, :groups, :roles, :auths, :profiles, :project, :keys, :expiry] properties.each do |property| it "should have a #{property} property" do user.attrclass(property).ancestors.should be_include(Puppet::Property) end it "should have documentation for its #{property} property" do user.attrclass(property).doc.should be_instance_of(String) end end list_properties = [:groups, :roles, :auths] list_properties.each do |property| it "should have a list '#{property}'" do user.attrclass(property).ancestors.should be_include(Puppet::Property::List) end end it "should have an ordered list 'profiles'" do user.attrclass(:profiles).ancestors.should be_include(Puppet::Property::OrderedList) end it "should have key values 'keys'" do user.attrclass(:keys).ancestors.should be_include(Puppet::Property::KeyValue) end describe "when retrieving all current values" do before do @user = user.new(:name => "foo", :uid => 10) end it "should return a hash containing values for all set properties" do @user[:gid] = 10 @user.property(:ensure).expects(:retrieve).returns :present @user.property(:uid).expects(:retrieve).returns 15 @user.property(:gid).expects(:retrieve).returns 15 values = @user.retrieve [@user.property(:uid), @user.property(:gid)].each { |property| values.should be_include(property) } end it "should set all values to :absent if the user is absent" do @user.property(:ensure).expects(:retrieve).returns :absent @user.property(:uid).expects(:retrieve).never @user.retrieve[@user.property(:uid)].should == :absent end it "should include the result of retrieving each property's current value if the user is present" do @user.property(:ensure).expects(:retrieve).returns :present @user.property(:uid).expects(:retrieve).returns 15 @user.retrieve[@user.property(:uid)].should == 15 end end describe "when managing the ensure property" do before do @ensure = user.attrclass(:ensure).new(:resource => @resource) end it "should support a :present value" do lambda { @ensure.should = :present }.should_not raise_error end it "should support an :absent value" do lambda { @ensure.should = :absent }.should_not raise_error end it "should call :create on the provider when asked to sync to the :present state" do @provider.expects(:create) @ensure.should = :present @ensure.sync end it "should call :delete on the provider when asked to sync to the :absent state" do @provider.expects(:delete) @ensure.should = :absent @ensure.sync end describe "and determining the current state" do it "should return :present when the provider indicates the user exists" do @provider.expects(:exists?).returns true @ensure.retrieve.should == :present end it "should return :absent when the provider indicates the user does not exist" do @provider.expects(:exists?).returns false @ensure.retrieve.should == :absent end end end describe "when managing the uid property" do it "should convert number-looking strings into actual numbers" do uid = user.attrclass(:uid).new(:resource => @resource) uid.should = "50" uid.should.must == 50 end it "should support UIDs as numbers" do uid = user.attrclass(:uid).new(:resource => @resource) uid.should = 50 uid.should.must == 50 end it "should :absent as a value" do uid = user.attrclass(:uid).new(:resource => @resource) uid.should = :absent uid.should.must == :absent end end describe "when managing the gid" do it "should :absent as a value" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = :absent gid.should.must == :absent end it "should convert number-looking strings into actual numbers" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = "50" gid.should.must == 50 end it "should support GIDs specified as integers" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = 50 gid.should.must == 50 end it "should support groups specified by name" do gid = user.attrclass(:gid).new(:resource => @resource) gid.should = "foo" gid.should.must == "foo" end describe "when testing whether in sync" do before do @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) end it "should return true if no 'should' values are set" do @gid = user.attrclass(:gid).new(:resource => @resource) @gid.must be_safe_insync(500) end it "should return true if any of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 @gid.must be_safe_insync(500) end it "should return false if none of the specified groups are equal to the current integer" do Puppet::Util.expects(:gid).with("foo").returns 300 Puppet::Util.expects(:gid).with("bar").returns 500 @gid.should_not be_safe_insync(700) end end describe "when syncing" do before do @gid = user.attrclass(:gid).new(:resource => @resource, :should => %w{foo bar}) end it "should use the first found, specified group as the desired value and send it to the provider" do Puppet::Util.expects(:gid).with("foo").returns nil Puppet::Util.expects(:gid).with("bar").returns 500 @provider.expects(:gid=).with 500 @gid.sync end end end describe "when managing expiry" do before do @expiry = user.attrclass(:expiry).new(:resource => @resource) end it "should fail if given an invalid date" do lambda { @expiry.should = "200-20-20" }.should raise_error(Puppet::Error) end end describe "when managing minimum password age" do before do @age = user.attrclass(:password_min_age).new(:resource => @resource) end it "should accept a negative minimum age" do expect { @age.should = -1 }.should_not raise_error end it "should fail with an empty minimum age" do expect { @age.should = '' }.should raise_error(Puppet::Error) end end describe "when managing maximum password age" do before do @age = user.attrclass(:password_max_age).new(:resource => @resource) end it "should accept a negative maximum age" do expect { @age.should = -1 }.should_not raise_error end it "should fail with an empty maximum age" do expect { @age.should = '' }.should raise_error(Puppet::Error) end end describe "when managing passwords" do before do @password = user.attrclass(:password).new(:resource => @resource, :should => "mypass") end it "should not include the password in the change log when adding the password" do @password.change_to_s(:absent, "mypass").should_not be_include("mypass") end it "should not include the password in the change log when changing the password" do @password.change_to_s("other", "mypass").should_not be_include("mypass") end it "should fail if a ':' is included in the password" do lambda { @password.should = "some:thing" }.should raise_error(Puppet::Error) end it "should allow the value to be set to :absent" do lambda { @password.should = :absent }.should_not raise_error end end describe "when manages_solaris_rbac is enabled" do before do @provider.stubs(:satisfies?).returns(false) @provider.expects(:satisfies?).with([:manages_solaris_rbac]).returns(true) end it "should support a :role value for ensure" do @ensure = user.attrclass(:ensure).new(:resource => @resource) lambda { @ensure.should = :role }.should_not raise_error end end describe "when user has roles" do before do # To test this feature, we have to support it. user.new(:name => "foo").provider.class.stubs(:feature?).returns(true) end it "should autorequire roles" do testuser = Puppet::Type.type(:user).new(:name => "testuser") testuser[:roles] = "testrole" testrole = Puppet::Type.type(:user).new(:name => "testrole") config = Puppet::Resource::Catalog.new :testing do |conf| [testuser, testrole].each { |resource| conf.add_resource resource } end Puppet::Type::User::ProviderDirectoryservice.stubs(:get_macosx_version_major).returns "10.5" rel = testuser.autorequire[0] rel.source.ref.should == testrole.ref rel.target.ref.should == testuser.ref end end end diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb index bbdaec3bc..73150af48 100755 --- a/spec/unit/type_spec.rb +++ b/spec/unit/type_spec.rb @@ -1,649 +1,660 @@ #!/usr/bin/env rspec require 'spec_helper' -describe Puppet::Type do +describe Puppet::Type, :'fails_on_windows' => true do + include PuppetSpec::Files + it "should include the Cacher module" do Puppet::Type.ancestors.should be_include(Puppet::Util::Cacher) end it "should consider a parameter to be valid if it is a valid parameter" do Puppet::Type.type(:mount).should be_valid_parameter(:path) end it "should consider a parameter to be valid if it is a valid property" do Puppet::Type.type(:mount).should be_valid_parameter(:fstype) end it "should consider a parameter to be valid if it is a valid metaparam" do Puppet::Type.type(:mount).should be_valid_parameter(:noop) end it "should use its catalog as its expirer" do catalog = Puppet::Resource::Catalog.new resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.catalog = catalog resource.expirer.should equal(catalog) end it "should do nothing when asked to expire when it has no catalog" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) lambda { resource.expire }.should_not raise_error end it "should be able to retrieve a property by name" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.property(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) end it "should be able to retrieve a parameter by name" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.parameter(:name).must be_instance_of(Puppet::Type.type(:mount).attrclass(:name)) end it "should be able to retrieve a property by name using the :parameter method" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) resource.parameter(:fstype).must be_instance_of(Puppet::Type.type(:mount).attrclass(:fstype)) end it "should be able to retrieve all set properties" do resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) props = resource.properties props.should_not be_include(nil) [:fstype, :ensure, :pass].each do |name| props.should be_include(resource.parameter(name)) end end it "should have a method for setting default values for resources" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:set_default) end it "should do nothing for attributes that have no defaults and no specified value" do Puppet::Type.type(:mount).new(:name => "foo").parameter(:noop).should be_nil end it "should have a method for adding tags" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:tags) end it "should use the tagging module" do Puppet::Type.type(:mount).ancestors.should be_include(Puppet::Util::Tagging) end it "should delegate to the tagging module when tags are added" do resource = Puppet::Type.type(:mount).new(:name => "foo") resource.stubs(:tag).with(:mount) resource.expects(:tag).with(:tag1, :tag2) resource.tags = [:tag1,:tag2] end it "should add the current type as tag" do resource = Puppet::Type.type(:mount).new(:name => "foo") resource.stubs(:tag) resource.expects(:tag).with(:mount) resource.tags = [:tag1,:tag2] end it "should have a method to know if the resource is exported" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:exported?) end it "should have a method to know if the resource is virtual" do Puppet::Type.type(:mount).new(:name => "foo").should respond_to(:virtual?) end it "should consider its version to be its catalog version" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.version = 50 catalog.add_resource resource resource.version.should == 50 end it "should consider its version to be zero if it has no catalog" do Puppet::Type.type(:mount).new(:name => "foo").version.should == 0 end it "should provide source_descriptors" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.version = 50 catalog.add_resource resource resource.source_descriptors.should == {:tags=>["mount", "foo"], :path=>"/Mount[foo]"} end it "should consider its type to be the name of its class" do Puppet::Type.type(:mount).new(:name => "foo").type.should == :mount end it "should use any provided noop value" do Puppet::Type.type(:mount).new(:name => "foo", :noop => true).must be_noop end it "should use the global noop value if none is provided" do Puppet[:noop] = true Puppet::Type.type(:mount).new(:name => "foo").must be_noop end it "should not be noop if in a non-host_config catalog" do resource = Puppet::Type.type(:mount).new(:name => "foo") catalog = Puppet::Resource::Catalog.new catalog.add_resource resource resource.should_not be_noop end describe "when creating an event" do before do @resource = Puppet::Type.type(:mount).new :name => "foo" end it "should have the resource's reference as the resource" do @resource.event.resource.should == "Mount[foo]" end it "should have the resource's log level as the default log level" do @resource[:loglevel] = :warning @resource.event.default_log_level.should == :warning end {:file => "/my/file", :line => 50, :tags => %{foo bar}}.each do |attr, value| it "should set the #{attr}" do @resource.stubs(attr).returns value @resource.event.send(attr).should == value end end it "should allow specification of event attributes" do @resource.event(:status => "noop").status.should == "noop" end end describe "when creating a provider" do before :each do @type = Puppet::Type.newtype(:provider_test_type) end after :each do @type.provider_hash.clear end it "should create a subclass of Puppet::Provider for the provider" do provider = @type.provide(:test_provider) provider.ancestors.should include(Puppet::Provider) end it "should use a parent class if specified" do parent_provider = @type.provide(:parent_provider) child_provider = @type.provide(:child_provider, :parent => parent_provider) child_provider.ancestors.should include(parent_provider) end it "should use a parent class if specified by name" do parent_provider = @type.provide(:parent_provider) child_provider = @type.provide(:child_provider, :parent => :parent_provider) child_provider.ancestors.should include(parent_provider) end it "should raise an error when the parent class can't be found" do expect { @type.provide(:child_provider, :parent => :parent_provider) }.to raise_error(Puppet::DevError, /Could not find parent provider.+parent_provider/) end it "should ensure its type has a 'provider' parameter" do @type.provide(:test_provider) @type.parameters.should include(:provider) end it "should remove a previously registered provider with the same name" do old_provider = @type.provide(:test_provider) new_provider = @type.provide(:test_provider) old_provider.should_not equal(new_provider) end it "should register itself as a provider for the type" do provider = @type.provide(:test_provider) provider.should == @type.provider(:test_provider) end it "should create a provider when a provider with the same name previously failed" do @type.provide(:test_provider) do raise "failed to create this provider" end rescue nil provider = @type.provide(:test_provider) provider.ancestors.should include(Puppet::Provider) provider.should == @type.provider(:test_provider) end end describe "when choosing a default provider" do it "should choose the provider with the highest specificity" do # Make a fake type type = Puppet::Type.newtype(:defaultprovidertest) do newparam(:name) do end end basic = type.provide(:basic) {} greater = type.provide(:greater) {} basic.stubs(:specificity).returns 1 greater.stubs(:specificity).returns 2 type.defaultprovider.should equal(greater) end end describe "when initializing" do describe "and passed a TransObject" do it "should fail" do trans = Puppet::TransObject.new("/foo", :mount) lambda { Puppet::Type.type(:mount).new(trans) }.should raise_error(Puppet::DevError) end end describe "and passed a Puppet::Resource instance" do it "should set its title to the title of the resource if the resource type is equal to the current type" do resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "/other"}) Puppet::Type.type(:mount).new(resource).title.should == "/foo" end it "should set its title to the resource reference if the resource type is not equal to the current type" do resource = Puppet::Resource.new(:user, "foo") Puppet::Type.type(:mount).new(resource).title.should == "User[foo]" end [:line, :file, :catalog, :exported, :virtual].each do |param| it "should copy '#{param}' from the resource if present" do resource = Puppet::Resource.new(:mount, "/foo") resource.send(param.to_s + "=", "foo") resource.send(param.to_s + "=", "foo") Puppet::Type.type(:mount).new(resource).send(param).should == "foo" end end it "should copy any tags from the resource" do resource = Puppet::Resource.new(:mount, "/foo") resource.tag "one", "two" tags = Puppet::Type.type(:mount).new(resource).tags tags.should be_include("one") tags.should be_include("two") end it "should copy the resource's parameters as its own" do resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:atboot => true, :fstype => "boo"}) params = Puppet::Type.type(:mount).new(resource).to_hash params[:fstype].should == "boo" params[:atboot].should == true end end describe "and passed a Hash" do it "should extract the title from the hash" do Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" end it "should work when hash keys are provided as strings" do Puppet::Type.type(:mount).new("title" => "/yay").title.should == "/yay" end it "should work when hash keys are provided as symbols" do Puppet::Type.type(:mount).new(:title => "/yay").title.should == "/yay" end it "should use the name from the hash as the title if no explicit title is provided" do Puppet::Type.type(:mount).new(:name => "/yay").title.should == "/yay" end it "should use the Resource Type's namevar to determine how to find the name in the hash" do - Puppet::Type.type(:file).new(:path => "/yay").title.should == "/yay" + yay = make_absolute('/yay') + Puppet::Type.type(:file).new(:path => yay).title.should == yay end [:catalog].each do |param| it "should extract '#{param}' from the hash if present" do Puppet::Type.type(:mount).new(:name => "/yay", param => "foo").send(param).should == "foo" end end it "should use any remaining hash keys as its parameters" do resource = Puppet::Type.type(:mount).new(:title => "/foo", :catalog => "foo", :atboot => true, :fstype => "boo") resource[:fstype].must == "boo" resource[:atboot].must == true end end it "should fail if any invalid attributes have been provided" do lambda { Puppet::Type.type(:mount).new(:title => "/foo", :nosuchattr => "whatever") }.should raise_error(Puppet::Error) end it "should set its name to the resource's title if the resource does not have a :name or namevar parameter set" do resource = Puppet::Resource.new(:mount, "/foo") Puppet::Type.type(:mount).new(resource).name.should == "/foo" end it "should fail if no title, name, or namevar are provided" do lambda { Puppet::Type.type(:file).new(:atboot => true) }.should raise_error(Puppet::Error) end it "should set the attributes in the order returned by the class's :allattrs method" do Puppet::Type.type(:mount).stubs(:allattrs).returns([:name, :atboot, :noop]) resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot", :noop => "whatever"}) set = [] Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| set << param true end.returns(stub_everything("a property")) Puppet::Type.type(:mount).new(resource) set[-1].should == :noop set[-2].should == :atboot end it "should always set the name and then default provider before anything else" do Puppet::Type.type(:mount).stubs(:allattrs).returns([:provider, :name, :atboot]) resource = Puppet::Resource.new(:mount, "/foo", :parameters => {:name => "myname", :atboot => "myboot"}) set = [] Puppet::Type.type(:mount).any_instance.stubs(:newattr).with do |param, hash| set << param true end.returns(stub_everything("a property")) Puppet::Type.type(:mount).new(resource) set[0].should == :name set[1].should == :provider end # This one is really hard to test :/ it "should each default immediately if no value is provided" do defaults = [] Puppet::Type.type(:package).any_instance.stubs(:set_default).with { |value| defaults << value; true } Puppet::Type.type(:package).new :name => "whatever" defaults[0].should == :provider end it "should retain a copy of the originally provided parameters" do Puppet::Type.type(:mount).new(:name => "foo", :atboot => true, :noop => false).original_parameters.should == {:atboot => true, :noop => false} end it "should delete the name via the namevar from the originally provided parameters" do - Puppet::Type.type(:file).new(:name => "/foo").original_parameters[:path].should be_nil + Puppet::Type.type(:file).new(:name => make_absolute('/foo')).original_parameters[:path].should be_nil end end it "should have a class method for converting a hash into a Puppet::Resource instance" do Puppet::Type.type(:mount).must respond_to(:hash2resource) end describe "when converting a hash to a Puppet::Resource instance" do before do @type = Puppet::Type.type(:mount) end it "should treat a :title key as the title of the resource" do @type.hash2resource(:name => "/foo", :title => "foo").title.should == "foo" end it "should use the name from the hash as the title if no explicit title is provided" do @type.hash2resource(:name => "foo").title.should == "foo" end it "should use the Resource Type's namevar to determine how to find the name in the hash" do @type.stubs(:key_attributes).returns([ :myname ]) @type.hash2resource(:myname => "foo").title.should == "foo" end [:catalog].each do |attr| it "should use any provided #{attr}" do @type.hash2resource(:name => "foo", attr => "eh").send(attr).should == "eh" end end it "should set all provided parameters on the resource" do @type.hash2resource(:name => "foo", :fstype => "boo", :boot => "fee").to_hash.should == {:name => "foo", :fstype => "boo", :boot => "fee"} end it "should not set the title as a parameter on the resource" do @type.hash2resource(:name => "foo", :title => "eh")[:title].should be_nil end it "should not set the catalog as a parameter on the resource" do @type.hash2resource(:name => "foo", :catalog => "eh")[:catalog].should be_nil end it "should treat hash keys equivalently whether provided as strings or symbols" do resource = @type.hash2resource("name" => "foo", "title" => "eh", "fstype" => "boo") resource.title.should == "eh" resource[:name].should == "foo" resource[:fstype].should == "boo" end end describe "when retrieving current property values" do before do @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) @resource.property(:ensure).stubs(:retrieve).returns :absent end it "should fail if its provider is unsuitable" do @resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present) @resource.provider.class.expects(:suitable?).returns false lambda { @resource.retrieve_resource }.should raise_error(Puppet::Error) end it "should return a Puppet::Resource instance with its type and title set appropriately" do result = @resource.retrieve_resource result.should be_instance_of(Puppet::Resource) result.type.should == "Mount" result.title.should == "foo" end it "should set the name of the returned resource if its own name and title differ" do @resource[:name] = "my name" @resource.title = "other name" @resource.retrieve_resource[:name].should == "my name" end it "should provide a value for all set properties" do values = @resource.retrieve_resource [:ensure, :fstype, :pass].each { |property| values[property].should_not be_nil } end it "should provide a value for 'ensure' even if no desired value is provided" do - @resource = Puppet::Type.type(:file).new(:path => "/my/file/that/can't/exist") + @resource = Puppet::Type.type(:file).new(:path => make_absolute("/my/file/that/can't/exist")) end it "should not call retrieve on non-ensure properties if the resource is absent and should consider the property absent" do @resource.property(:ensure).expects(:retrieve).returns :absent @resource.property(:fstype).expects(:retrieve).never @resource.retrieve_resource[:fstype].should == :absent end it "should include the result of retrieving each property's current value if the resource is present" do @resource.property(:ensure).expects(:retrieve).returns :present @resource.property(:fstype).expects(:retrieve).returns 15 @resource.retrieve_resource[:fstype] == 15 end end describe ".title_patterns" do describe "when there's one namevar" do before do @type_class = Puppet::Type.type(:notify) @type_class.stubs(:key_attributes).returns([:one]) end it "should have a default pattern for when there's one namevar" do patterns = @type_class.title_patterns patterns.length.should == 1 patterns[0].length.should == 2 end it "should have a regexp that captures the entire string" do patterns = @type_class.title_patterns string = "abc\n\tdef" patterns[0][0] =~ string $1.should == "abc\n\tdef" end end end describe "when in a catalog" do before do @catalog = Puppet::Resource::Catalog.new @container = Puppet::Type.type(:component).new(:name => "container") - @one = Puppet::Type.type(:file).new(:path => "/file/one") - @two = Puppet::Type.type(:file).new(:path => "/file/two") + @one = Puppet::Type.type(:file).new(:path => make_absolute("/file/one")) + @two = Puppet::Type.type(:file).new(:path => make_absolute("/file/two")) @catalog.add_resource @container @catalog.add_resource @one @catalog.add_resource @two @catalog.add_edge @container, @one @catalog.add_edge @container, @two end it "should have no parent if there is no in edge" do @container.parent.should be_nil end it "should set its parent to its in edge" do @one.parent.ref.should == @container.ref end after do @catalog.clear(true) end end it "should have a 'stage' metaparam" do Puppet::Type.metaparamclass(:stage).should be_instance_of(Class) end end -describe Puppet::Type::RelationshipMetaparam do +describe Puppet::Type::RelationshipMetaparam, :fails_on_windows => true do + include PuppetSpec::Files + it "should be a subclass of Puppet::Parameter" do Puppet::Type::RelationshipMetaparam.superclass.should equal(Puppet::Parameter) end it "should be able to produce a list of subclasses" do Puppet::Type::RelationshipMetaparam.should respond_to(:subclasses) end - describe "when munging relationships" do + describe "when munging relationships", :'fails_on_windows' => true do before do - @resource = Puppet::Type.type(:mount).new :name => "/foo" + @path = make_absolute('/foo') + @resource = Puppet::Type.type(:mount).new :name => @path @metaparam = Puppet::Type.metaparamclass(:require).new :resource => @resource end it "should accept Puppet::Resource instances" do - ref = Puppet::Resource.new(:file, "/foo") + ref = Puppet::Resource.new(:file, @path) @metaparam.munge(ref)[0].should equal(ref) end it "should turn any string into a Puppet::Resource" do @metaparam.munge("File[/ref]")[0].should be_instance_of(Puppet::Resource) end end it "should be able to validate relationships" do Puppet::Type.metaparamclass(:require).new(:resource => mock("resource")).should respond_to(:validate_relationship) end it "should fail if any specified resource is not found in the catalog" do catalog = mock 'catalog' resource = stub 'resource', :catalog => catalog, :ref => "resource" param = Puppet::Type.metaparamclass(:require).new(:resource => resource, :value => %w{Foo[bar] Class[test]}) catalog.expects(:resource).with("Foo[bar]").returns "something" catalog.expects(:resource).with("Class[Test]").returns nil param.expects(:fail).with { |string| string.include?("Class[Test]") } param.validate_relationship end end -describe Puppet::Type.metaparamclass(:check) do +describe Puppet::Type.metaparamclass(:check), :fails_on_windows => true do + include PuppetSpec::Files + it "should warn and create an instance of ':audit'" do - file = Puppet::Type.type(:file).new :path => "/foo" + file = Puppet::Type.type(:file).new :path => make_absolute('/foo') file.expects(:warning) file[:check] = :mode file[:audit].should == [:mode] end end -describe Puppet::Type.metaparamclass(:audit) do +describe Puppet::Type.metaparamclass(:audit), :fails_on_windows => true do + include PuppetSpec::Files + before do - @resource = Puppet::Type.type(:file).new :path => "/foo" + @resource = Puppet::Type.type(:file).new :path => make_absolute('/foo') end it "should default to being nil" do @resource[:audit].should be_nil end it "should specify all possible properties when asked to audit all properties" do @resource[:audit] = :all list = @resource.class.properties.collect { |p| p.name } @resource[:audit].should == list end it "should accept the string 'all' to specify auditing all possible properties" do @resource[:audit] = 'all' list = @resource.class.properties.collect { |p| p.name } @resource[:audit].should == list end it "should fail if asked to audit an invalid property" do lambda { @resource[:audit] = :foobar }.should raise_error(Puppet::Error) end it "should create an attribute instance for each auditable property" do @resource[:audit] = :mode @resource.parameter(:mode).should_not be_nil end it "should accept properties specified as a string" do @resource[:audit] = "mode" @resource.parameter(:mode).should_not be_nil end it "should not create attribute instances for parameters, only properties" do @resource[:audit] = :noop @resource.parameter(:noop).should be_nil end describe "when generating the uniqueness key" do it "should include all of the key_attributes in alphabetical order by attribute name" do Puppet::Type.type(:file).stubs(:key_attributes).returns [:path, :mode, :owner] Puppet::Type.type(:file).stubs(:title_patterns).returns( [ [ /(.*)/, [ [:path, lambda{|x| x} ] ] ] ] ) - res = Puppet::Type.type(:file).new( :title => '/my/file', :path => '/my/file', :owner => 'root', :content => 'hello' ) - res.uniqueness_key.should == [ nil, 'root', '/my/file'] + myfile = make_absolute('/my/file') + res = Puppet::Type.type(:file).new( :title => myfile, :path => myfile, :owner => 'root', :content => 'hello' ) + res.uniqueness_key.should == [ nil, 'root', myfile] end end end diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb index d61b7689e..100975f27 100755 --- a/spec/unit/util/autoload_spec.rb +++ b/spec/unit/util/autoload_spec.rb @@ -1,119 +1,127 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/autoload' describe Puppet::Util::Autoload do + include PuppetSpec::Files + before do @autoload = Puppet::Util::Autoload.new("foo", "tmp") @autoload.stubs(:eachdir).yields "/my/dir" end it "should use the Cacher module" do Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Cacher) end describe "when building the search path" do + before :each do + @dira = make_absolute('/a') + @dirb = make_absolute('/b') + @dirc = make_absolute('/c') + end + it "should collect all of the plugins and lib directories that exist in the current environment's module path" do Puppet.settings.expects(:value).with(:environment).returns "foo" - Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a:/b:/c" - Dir.expects(:entries).with("/a").returns %w{one two} - Dir.expects(:entries).with("/b").returns %w{one two} + Puppet.settings.expects(:value).with(:modulepath, :foo).returns "#{@dira}#{File::PATH_SEPARATOR}#{@dirb}#{File::PATH_SEPARATOR}#{@dirc}" + Dir.expects(:entries).with(@dira).returns %w{one two} + Dir.expects(:entries).with(@dirb).returns %w{one two} FileTest.stubs(:directory?).returns false - FileTest.expects(:directory?).with("/a").returns true - FileTest.expects(:directory?).with("/b").returns true - %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib}.each do |d| + FileTest.expects(:directory?).with(@dira).returns true + FileTest.expects(:directory?).with(@dirb).returns true + ["#{@dira}/one/plugins", "#{@dira}/two/lib", "#{@dirb}/one/plugins", "#{@dirb}/two/lib"].each do |d| FileTest.expects(:directory?).with(d).returns true end - @autoload.module_directories.should == %w{/a/one/plugins /a/two/lib /b/one/plugins /b/two/lib} + @autoload.module_directories.should == ["#{@dira}/one/plugins", "#{@dira}/two/lib", "#{@dirb}/one/plugins", "#{@dirb}/two/lib"] end it "should not look for lib directories in directories starting with '.'" do Puppet.settings.expects(:value).with(:environment).returns "foo" - Puppet.settings.expects(:value).with(:modulepath, :foo).returns "/a" - Dir.expects(:entries).with("/a").returns %w{. ..} - - FileTest.expects(:directory?).with("/a").returns true - FileTest.expects(:directory?).with("/a/./lib").never - FileTest.expects(:directory?).with("/a/./plugins").never - FileTest.expects(:directory?).with("/a/../lib").never - FileTest.expects(:directory?).with("/a/../plugins").never + Puppet.settings.expects(:value).with(:modulepath, :foo).returns @dira + Dir.expects(:entries).with(@dira).returns %w{. ..} + + FileTest.expects(:directory?).with(@dira).returns true + FileTest.expects(:directory?).with("#{@dira}/./lib").never + FileTest.expects(:directory?).with("#{@dira}/./plugins").never + FileTest.expects(:directory?).with("#{@dira}/../lib").never + FileTest.expects(:directory?).with("#{@dira}/../plugins").never @autoload.module_directories end it "should include the module directories, the Puppet libdir, and all of the Ruby load directories" do Puppet.stubs(:[]).with(:libdir).returns(%w{/libdir1 /lib/dir/two /third/lib/dir}.join(File::PATH_SEPARATOR)) @autoload.expects(:module_directories).returns %w{/one /two} @autoload.search_directories.should == %w{/one /two /libdir1 /lib/dir/two /third/lib/dir} + $LOAD_PATH end it "should include in its search path all of the unique search directories that have a subdirectory matching the autoload path" do @autoload = Puppet::Util::Autoload.new("foo", "loaddir") @autoload.expects(:search_directories).returns %w{/one /two /three /three} FileTest.expects(:directory?).with("/one/loaddir").returns true FileTest.expects(:directory?).with("/two/loaddir").returns false FileTest.expects(:directory?).with("/three/loaddir").returns true @autoload.searchpath.should == ["/one/loaddir", "/three/loaddir"] end end it "should include its FileCache module" do Puppet::Util::Autoload.ancestors.should be_include(Puppet::Util::Autoload::FileCache) end describe "when loading a file" do before do @autoload.stubs(:searchpath).returns %w{/a} end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do @autoload.stubs(:file_exist?).returns true Kernel.expects(:load).raises error lambda { @autoload.load("foo") }.should raise_error(Puppet::Error) end end it "should not raise an error if the file is missing" do @autoload.load("foo").should == false end it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do @autoload.stubs(:file_exist?).returns true Kernel.stubs(:load) @autoload.load("myfile") $LOADED_FEATURES.should be_include("tmp/myfile.rb") end end describe "when loading all files" do before do @autoload.stubs(:searchpath).returns %w{/a} Dir.stubs(:glob).returns "/path/to/file.rb" @autoload.class.stubs(:loaded?).returns(false) end [RuntimeError, LoadError, SyntaxError].each do |error| it "should die an if a #{error.to_s} exception is thrown", :'fails_on_ruby_1.9.2' => true do Kernel.expects(:require).raises error lambda { @autoload.loadall }.should raise_error(Puppet::Error) end end it "should require the full path to the file", :'fails_on_ruby_1.9.2' => true do Kernel.expects(:require).with("/path/to/file.rb") @autoload.loadall end end end diff --git a/spec/unit/util/backups_spec.rb b/spec/unit/util/backups_spec.rb index 611c19304..d2f36a6e6 100755 --- a/spec/unit/util/backups_spec.rb +++ b/spec/unit/util/backups_spec.rb @@ -1,157 +1,160 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/backups' describe Puppet::Util::Backups do + include PuppetSpec::Files + before do FileTest.stubs(:exists?).returns true + @nosuchfile = make_absolute('/no/such/file') end describe "when backing up a file" do it "should noop if the file does not exist" do FileTest.expects(:exists?).returns false - file = Puppet::Type.type(:file).new(:name => '/no/such/file') + file = Puppet::Type.type(:file).new(:name => @nosuchfile) file.expects(:bucket).never file.perform_backup end it "should succeed silently if self[:backup] is false" do - file = Puppet::Type.type(:file).new(:name => '/no/such/file', :backup => false) + file = Puppet::Type.type(:file).new(:name => @nosuchfile, :backup => false) file.expects(:bucket).never FileTest.expects(:exists?).never file.perform_backup end it "a bucket should be used when provided" do - path = '/my/file' + path = make_absolute('/my/file') File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') bucket = stub('bucket', 'name' => 'foo') file.stubs(:bucket).returns bucket bucket.expects(:backup).with(path).returns("mysum") file.perform_backup end it "should propagate any exceptions encountered when backing up to a filebucket" do - path = '/my/file' + path = make_absolute('/my/file') File.stubs(:stat).with(path).returns(mock('stat', :ftype => 'file')) file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') bucket = stub('bucket', 'name' => 'foo') file.stubs(:bucket).returns bucket bucket.expects(:backup).raises ArgumentError lambda { file.perform_backup }.should raise_error(ArgumentError) end describe "and no filebucket is configured" do it "should remove any local backup if one exists" do - path = '/my/file' + path = make_absolute('/my/file') FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") File.expects(:unlink).with(backup) FileUtils.stubs(:cp_r) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup end it "should fail when the old backup can't be removed" do - path = '/my/file' + path = make_absolute('/my/file') FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file") File.expects(:unlink).raises ArgumentError FileUtils.expects(:cp_r).never file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') lambda { file.perform_backup }.should raise_error(Puppet::Error) end it "should not try to remove backups that don't exist" do - path = '/my/file' + path = make_absolute('/my/file') FileTest.stubs(:exists?).returns true backup = path + ".foo" File.expects(:lstat).with(backup).raises(Errno::ENOENT) File.expects(:unlink).never FileUtils.stubs(:cp_r) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup end it "a copy should be created in the local directory" do - path = '/my/file' + path = make_absolute('/my/file') FileTest.stubs(:exists?).with(path).returns true FileUtils.expects(:cp_r).with(path, path + ".foo", :preserve => true) file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') file.perform_backup.should be_true end it "should propagate exceptions if no backup can be created" do - path = '/my/file' + path = make_absolute('/my/file') FileTest.stubs(:exists?).with(path).returns true FileUtils.expects(:cp_r).raises ArgumentError file = Puppet::Type.type(:file).new(:name => path, :backup => '.foo') lambda { file.perform_backup }.should raise_error(Puppet::Error) end end end describe "when backing up a directory" do it "a bucket should work when provided" do - path = '/my/dir' + path = make_absolute('/my/dir') File.stubs(:file?).returns true - Find.expects(:find).with(path).yields("/my/dir/file") + Find.expects(:find).with(path).yields(make_absolute("/my/dir/file")) bucket = stub('bucket', :name => "eh") - bucket.expects(:backup).with("/my/dir/file").returns true + bucket.expects(:backup).with(make_absolute("/my/dir/file")).returns true file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo') file.stubs(:bucket).returns bucket File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) file.perform_backup end it "should do nothing when recursing" do - path = '/my/dir' + path = make_absolute('/my/dir') bucket = stub('bucket', :name => "eh") bucket.expects(:backup).never file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo', :recurse => true) file.stubs(:bucket).returns bucket File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory')) Find.expects(:find).never file.perform_backup end end end diff --git a/spec/unit/util/execution_stub_spec.rb b/spec/unit/util/execution_stub_spec.rb index 34987689c..9cd15ca6a 100755 --- a/spec/unit/util/execution_stub_spec.rb +++ b/spec/unit/util/execution_stub_spec.rb @@ -1,34 +1,34 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Util::ExecutionStub do it "should use the provided stub code when 'set' is called" do Puppet::Util::ExecutionStub.set do |command, options| command.should == ['/bin/foo', 'bar'] "stub output" end Puppet::Util::ExecutionStub.current_value.should_not == nil Puppet::Util.execute(['/bin/foo', 'bar']).should == "stub output" end it "should automatically restore normal execution at the conclusion of each spec test" do # Note: this test relies on the previous test creating a stub. Puppet::Util::ExecutionStub.current_value.should == nil end - it "should restore normal execution after 'reset' is called" do + it "should restore normal execution after 'reset' is called", :fails_on_windows => true do true_command = Puppet::Util.which('true') # Note: "true" exists at different paths in different OSes stub_call_count = 0 Puppet::Util::ExecutionStub.set do |command, options| command.should == [true_command] stub_call_count += 1 'stub called' end Puppet::Util.execute([true_command]).should == 'stub called' stub_call_count.should == 1 Puppet::Util::ExecutionStub.reset Puppet::Util::ExecutionStub.current_value.should == nil Puppet::Util.execute([true_command]).should == '' stub_call_count.should == 1 end end diff --git a/spec/unit/util/log_spec.rb b/spec/unit/util/log_spec.rb index 1baa0d5af..39da4b010 100755 --- a/spec/unit/util/log_spec.rb +++ b/spec/unit/util/log_spec.rb @@ -1,226 +1,228 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/log' describe Puppet::Util::Log do + include PuppetSpec::Files + it "should write a given message to the specified destination" do arraydest = [] Puppet::Util::Log.newdestination(Puppet::Test::LogCollector.new(arraydest)) Puppet::Util::Log.new(:level => :notice, :message => "foo") message = arraydest.last.message message.should == "foo" end describe Puppet::Util::Log::DestConsole do before do @console = Puppet::Util::Log::DestConsole.new end it "should colorize if Puppet[:color] is :ansi" do Puppet[:color] = :ansi @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" end it "should colorize if Puppet[:color] is 'yes'" do Puppet[:color] = "yes" @console.colorize(:alert, "abc").should == "\e[0;31mabc\e[0m" end it "should htmlize if Puppet[:color] is :html" do Puppet[:color] = :html @console.colorize(:alert, "abc").should == "abc" end it "should do nothing if Puppet[:color] is false" do Puppet[:color] = false @console.colorize(:alert, "abc").should == "abc" end it "should do nothing if Puppet[:color] is invalid" do Puppet[:color] = "invalid option" @console.colorize(:alert, "abc").should == "abc" end end describe "instances" do before do Puppet::Util::Log.stubs(:newmessage) end [:level, :message, :time, :remote].each do |attr| it "should have a #{attr} attribute" do log = Puppet::Util::Log.new :level => :notice, :message => "A test message" log.should respond_to(attr) log.should respond_to(attr.to_s + "=") end end it "should fail if created without a level" do lambda { Puppet::Util::Log.new(:message => "A test message") }.should raise_error(ArgumentError) end it "should fail if created without a message" do lambda { Puppet::Util::Log.new(:level => :notice) }.should raise_error(ArgumentError) end it "should make available the level passed in at initialization" do Puppet::Util::Log.new(:level => :notice, :message => "A test message").level.should == :notice end it "should make available the message passed in at initialization" do Puppet::Util::Log.new(:level => :notice, :message => "A test message").message.should == "A test message" end # LAK:NOTE I don't know why this behavior is here, I'm just testing what's in the code, # at least at first. it "should always convert messages to strings" do Puppet::Util::Log.new(:level => :notice, :message => :foo).message.should == "foo" end it "should flush the log queue when the first destination is specified" do Puppet::Util::Log.close_all Puppet::Util::Log.expects(:flushqueue) Puppet::Util::Log.newdestination(:console) end it "should convert the level to a symbol if it's passed in as a string" do Puppet::Util::Log.new(:level => "notice", :message => :foo).level.should == :notice end it "should fail if the level is not a symbol or string", :'fails_on_ruby_1.9.2' => true do lambda { Puppet::Util::Log.new(:level => 50, :message => :foo) }.should raise_error(ArgumentError) end it "should fail if the provided level is not valid" do Puppet::Util::Log.expects(:validlevel?).with(:notice).returns false lambda { Puppet::Util::Log.new(:level => :notice, :message => :foo) }.should raise_error(ArgumentError) end it "should set its time to the initialization time" do time = mock 'time' Time.expects(:now).returns time Puppet::Util::Log.new(:level => "notice", :message => :foo).time.should equal(time) end it "should make available any passed-in tags" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{foo bar}) log.tags.should be_include("foo") log.tags.should be_include("bar") end it "should use an passed-in source" do Puppet::Util::Log.any_instance.expects(:source=).with "foo" Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => "foo") end [:file, :line].each do |attr| it "should use #{attr} if provided" do Puppet::Util::Log.any_instance.expects(attr.to_s + "=").with "foo" Puppet::Util::Log.new(:level => "notice", :message => :foo, attr => "foo") end end it "should default to 'Puppet' as its source" do Puppet::Util::Log.new(:level => "notice", :message => :foo).source.should == "Puppet" end it "should register itself with Log" do Puppet::Util::Log.expects(:newmessage) Puppet::Util::Log.new(:level => "notice", :message => :foo) end it "should update Log autoflush when Puppet[:autoflush] is set" do Puppet::Util::Log.expects(:autoflush=).once.with(true) Puppet[:autoflush] = true end it "should have a method for determining if a tag is present" do Puppet::Util::Log.new(:level => "notice", :message => :foo).should respond_to(:tagged?) end it "should match a tag if any of the tags are equivalent to the passed tag as a string" do Puppet::Util::Log.new(:level => "notice", :message => :foo, :tags => %w{one two}).should be_tagged(:one) end it "should tag itself with its log level" do Puppet::Util::Log.new(:level => "notice", :message => :foo).should be_tagged(:notice) end it "should return its message when converted to a string" do Puppet::Util::Log.new(:level => "notice", :message => :foo).to_s.should == "foo" end it "should include its time, source, level, and message when prepared for reporting" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo) report = log.to_report report.should be_include("notice") report.should be_include("foo") report.should be_include(log.source) report.should be_include(log.time.to_s) end describe "when setting the source as a RAL object" do it "should tag itself with any tags the source has" do - source = Puppet::Type.type(:file).new :path => "/foo/bar" + source = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar") log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) source.tags.each do |tag| log.tags.should be_include(tag) end end it "should use the source_descriptors" do source = stub "source" source.stubs(:source_descriptors).returns(:tags => ["tag","tag2"], :path => "path", :version => 100) log = Puppet::Util::Log.new(:level => "notice", :message => :foo) log.expects(:tag).with("tag") log.expects(:tag).with("tag2") log.source = source log.source.should == "path" end it "should copy over any file and line information" do - source = Puppet::Type.type(:file).new :path => "/foo/bar" + source = Puppet::Type.type(:file).new :path => make_absolute("/foo/bar") source.file = "/my/file" source.line = 50 log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) log.file.should == "/my/file" log.line.should == 50 end end describe "when setting the source as a non-RAL object" do it "should not try to copy over file, version, line, or tag information" do source = Puppet::Module.new("foo") source.expects(:file).never log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :source => source) end end end describe "to_yaml", :'fails_on_ruby_1.9.2' => true do it "should not include the @version attribute" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :version => 100) log.to_yaml_properties.should_not include('@version') end it "should include attributes @level, @message, @source, @tags, and @time" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :version => 100) log.to_yaml_properties.should == %w{@level @message @source @tags @time} end it "should include attributes @file and @line if specified" do log = Puppet::Util::Log.new(:level => "notice", :message => :foo, :file => "foo", :line => 35) log.to_yaml_properties.should include('@file') log.to_yaml_properties.should include('@line') end end end diff --git a/spec/unit/util/logging_spec.rb b/spec/unit/util/logging_spec.rb index 6a77e70ef..2953f54a4 100755 --- a/spec/unit/util/logging_spec.rb +++ b/spec/unit/util/logging_spec.rb @@ -1,119 +1,119 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/logging' class LoggingTester include Puppet::Util::Logging end describe Puppet::Util::Logging do before do @logger = LoggingTester.new end Puppet::Util::Log.eachlevel do |level| it "should have a method for sending '#{level}' logs" do @logger.should respond_to(level) end end it "should have a method for sending a log with a specified log level" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" and args[:level] == "loglevel" and args[:message] == "mymessage" } @logger.send_log "loglevel", "mymessage" end describe "when sending a log" do it "should use the Log's 'create' entrance method" do Puppet::Util::Log.expects(:create) @logger.notice "foo" end it "should send itself converted to a string as the log source" do @logger.expects(:to_s).returns "I'm a string!" Puppet::Util::Log.expects(:create).with { |args| args[:source] == "I'm a string!" } @logger.notice "foo" end it "should queue logs sent without a specified destination" do Puppet::Util::Log.close_all Puppet::Util::Log.expects(:queuemessage) @logger.notice "foo" end - it "should use the path of any provided resource type" do + it "should use the path of any provided resource type", :fails_on_windows => true do resource = Puppet::Type.type(:mount).new :name => "foo" resource.expects(:path).returns "/path/to/mount".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/mount" } resource.notice "foo" end - it "should use the path of any provided resource parameter" do + it "should use the path of any provided resource parameter", :fails_on_windows => true do resource = Puppet::Type.type(:mount).new :name => "foo" param = resource.parameter(:name) param.expects(:path).returns "/path/to/param".to_sym Puppet::Util::Log.expects(:create).with { |args| args[:source] == "/path/to/param" } param.notice "foo" end it "should send the provided argument as the log message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo" } @logger.notice "foo" end it "should join any provided arguments into a single string for the message" do Puppet::Util::Log.expects(:create).with { |args| args[:message] == "foo bar baz" } @logger.notice ["foo", "bar", "baz"] end [:file, :line, :tags].each do |attr| it "should include #{attr} if available" do @logger.singleton_class.send(:attr_accessor, attr) @logger.send(attr.to_s + "=", "myval") Puppet::Util::Log.expects(:create).with { |args| args[attr] == "myval" } @logger.notice "foo" end end end describe "when sending a deprecation warning" do before do @logger.clear_deprecation_warnings end it "should the message with warn" do @logger.expects(:warning).with('foo') @logger.deprecation_warning 'foo' end it "should only log each unique message once" do @logger.expects(:warning).with('foo').once 5.times { @logger.deprecation_warning 'foo' } end it "should only log the first 100 messages" do (1..100).each { |i| @logger.expects(:warning).with(i).once @logger.deprecation_warning i } @logger.expects(:warning).with(101).never @logger.deprecation_warning 101 end end end diff --git a/spec/unit/util/network_device/config_spec.rb b/spec/unit/util/network_device/config_spec.rb index d69358a92..d9bd3d979 100755 --- a/spec/unit/util/network_device/config_spec.rb +++ b/spec/unit/util/network_device/config_spec.rb @@ -1,101 +1,103 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/network_device/config' describe Puppet::Util::NetworkDevice::Config do + include PuppetSpec::Files + before(:each) do - Puppet[:deviceconfig] = "/dummy" - FileTest.stubs(:exists?).with("/dummy").returns(true) + Puppet[:deviceconfig] = make_absolute("/dummy") + FileTest.stubs(:exists?).with(make_absolute("/dummy")).returns(true) end describe "when initializing" do before :each do Puppet::Util::NetworkDevice::Config.any_instance.stubs(:read) end it "should use the deviceconfig setting as pathname" do - Puppet.expects(:[]).with(:deviceconfig).returns("/dummy") + Puppet.expects(:[]).with(:deviceconfig).returns(make_absolute("/dummy")) Puppet::Util::NetworkDevice::Config.new end it "should raise an error if no file is defined finally" do Puppet.expects(:[]).with(:deviceconfig).returns(nil) lambda { Puppet::Util::NetworkDevice::Config.new }.should raise_error(Puppet::DevError) end it "should read and parse the file" do Puppet::Util::NetworkDevice::Config.any_instance.expects(:read) Puppet::Util::NetworkDevice::Config.new end end describe "when parsing device" do before :each do @config = Puppet::Util::NetworkDevice::Config.new @config.stubs(:changed?).returns(true) @fd = stub 'fd' File.stubs(:open).yields(@fd) end it "should skip comments" do @fd.stubs(:each).yields(' # comment') OpenStruct.expects(:new).never @config.read end it "should increment line number even on commented lines" do @fd.stubs(:each).multiple_yields(' # comment','[router.puppetlabs.com]') @config.read @config.devices.should be_include('router.puppetlabs.com') end it "should skip blank lines" do @fd.stubs(:each).yields(' ') @config.read @config.devices.should be_empty end it "should produce the correct line number" do @fd.stubs(:each).multiple_yields(' ', '[router.puppetlabs.com]') @config.read @config.devices['router.puppetlabs.com'].line.should == 2 end it "should throw an error if the current device already exists" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', '[router.puppetlabs.com]') lambda { @config.read }.should raise_error end it "should create a new device for each found device line" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', '[swith.puppetlabs.com]') @config.read @config.devices.size.should == 2 end it "should parse the device type" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', 'type cisco') @config.read @config.devices['router.puppetlabs.com'].provider.should == 'cisco' end it "should parse the device url" do @fd.stubs(:each).multiple_yields('[router.puppetlabs.com]', 'type cisco', 'url ssh://test/') @config.read @config.devices['router.puppetlabs.com'].url.should == 'ssh://test/' end end end diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb index 92b50e09b..4c2c79e88 100755 --- a/spec/unit/util/rdoc/parser_spec.rb +++ b/spec/unit/util/rdoc/parser_spec.rb @@ -1,562 +1,564 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/resource/type_collection' require 'puppet/util/rdoc/parser' require 'puppet/util/rdoc/code_objects' require 'rdoc/options' require 'rdoc/rdoc' describe RDoc::Parser, :'fails_on_ruby_1.9.2' => true do + include PuppetSpec::Files + before :each do File.stubs(:stat).with("init.pp") @top_level = stub_everything 'toplevel', :file_relative_name => "init.pp" @parser = RDoc::Parser.new(@top_level, "module/manifests/init.pp", nil, Options.instance, RDoc::Stats.new) end describe "when scanning files" do it "should parse puppet files with the puppet parser" do @parser.stubs(:scan_top_level) parser = stub 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once parser.expects(:file=).with("module/manifests/init.pp") - parser.expects(:file=).with("/dev/null/manifests/site.pp") + parser.expects(:file=).with(make_absolute("/dev/null/manifests/site.pp")) @parser.scan end it "should scan the ast for Puppet files" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @parser.expects(:scan_top_level) @parser.scan end it "should return a PuppetTopLevel to RDoc" do parser = stub_everything 'parser' Puppet::Parser::Parser.stubs(:new).returns(parser) parser.expects(:parse).returns(Puppet::Parser::AST::Hostclass.new('')).at_least_once @parser.expects(:scan_top_level) @parser.scan.should be_a(RDoc::PuppetTopLevel) end it "should scan the top level even if the file has already parsed" do known_type = stub 'known_types' env = stub 'env' Puppet::Node::Environment.stubs(:new).returns(env) env.stubs(:known_resource_types).returns(known_type) known_type.expects(:watching_file?).with("module/manifests/init.pp").returns(true) @parser.expects(:scan_top_level) @parser.scan end end describe "when scanning top level entities" do before :each do @resource_type_collection = resource_type_collection = stub_everything('resource_type_collection') @parser.instance_eval { @known_resource_types = resource_type_collection } @parser.stubs(:split_module).returns("module") @topcontainer = stub_everything 'topcontainer' @container = stub_everything 'container' @module = stub_everything 'module' @container.stubs(:add_module).returns(@module) @parser.stubs(:get_class_or_module).returns([@container, "module"]) end it "should read any present README as module documentation" do FileTest.stubs(:readable?).returns(true) File.stubs(:open).returns("readme") @parser.stubs(:parse_elements) @module.expects(:comment=).with("readme") @parser.scan_top_level(@topcontainer) end it "should tell the container its module name" do @parser.stubs(:parse_elements) @topcontainer.expects(:module_name=).with("module") @parser.scan_top_level(@topcontainer) end it "should not document our toplevel if it isn't a valid module" do @parser.stubs(:split_module).returns(nil) @topcontainer.expects(:document_self=).with(false) @parser.expects(:parse_elements).never @parser.scan_top_level(@topcontainer) end it "should set the module as global if we parse the global manifests (ie __site__ module)" do @parser.stubs(:split_module).returns(RDoc::Parser::SITE) @parser.stubs(:parse_elements) @topcontainer.expects(:global=).with(true) @parser.scan_top_level(@topcontainer) end it "should attach this module container to the toplevel container" do @parser.stubs(:parse_elements) @container.expects(:add_module).with(RDoc::PuppetModule, "module").returns(@module) @parser.scan_top_level(@topcontainer) end it "should defer ast parsing to parse_elements for this module" do @parser.expects(:parse_elements).with(@module) @parser.scan_top_level(@topcontainer) end it "should defer plugins parsing to parse_plugins for this module" do @parser.input_file_name = "module/lib/puppet/parser/function.rb" @parser.expects(:parse_plugins).with(@module) @parser.scan_top_level(@topcontainer) end end describe "when finding modules from filepath" do before :each do Puppet::Module.stubs(:modulepath).returns("/path/to/modules") end it "should return the module name for modulized puppet manifests" do File.stubs(:expand_path).returns("/path/to/module/manifests/init.pp") File.stubs(:identical?).with("/path/to", "/path/to/modules").returns(true) @parser.split_module("/path/to/modules/mymodule/manifests/init.pp").should == "module" end it "should return for manifests not under module path" do File.stubs(:expand_path).returns("/path/to/manifests/init.pp") File.stubs(:identical?).returns(false) @parser.split_module("/path/to/manifests/init.pp").should == RDoc::Parser::SITE end end describe "when parsing AST elements" do before :each do @klass = stub_everything 'klass', :file => "module/manifests/init.pp", :name => "myclass", :type => :hostclass @definition = stub_everything 'definition', :file => "module/manifests/init.pp", :type => :definition, :name => "mydef" @node = stub_everything 'node', :file => "module/manifests/init.pp", :type => :node, :name => "mynode" @resource_type_collection = resource_type_collection = Puppet::Resource::TypeCollection.new("env") @parser.instance_eval { @known_resource_types = resource_type_collection } @container = stub_everything 'container' end it "should document classes in the parsed file" do @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container) @parser.parse_elements(@container) end it "should not document class parsed in an other file" do @klass.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_hostclass(@klass) @parser.expects(:document_class).with("myclass", @klass, @container).never @parser.parse_elements(@container) end it "should document vardefs for the main class" do @klass.stubs(:name).returns :main @resource_type_collection.add_hostclass(@klass) code = stub 'code', :is_a? => false @klass.stubs(:name).returns("") @klass.stubs(:code).returns(code) @parser.expects(:scan_for_vardef).with(@container, code) @parser.parse_elements(@container) end it "should document definitions in the parsed file" do @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container) @parser.parse_elements(@container) end it "should not document definitions parsed in an other file" do @definition.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_definition(@definition) @parser.expects(:document_define).with("mydef", @definition, @container).never @parser.parse_elements(@container) end it "should document nodes in the parsed file" do @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container) @parser.parse_elements(@container) end it "should not document node parsed in an other file" do @node.stubs(:file).returns("/not/same/path/file.pp") @resource_type_collection.add_node(@node) @parser.expects(:document_node).with("mynode", @node, @container).never @parser.parse_elements(@container) end end describe "when documenting definition" do before(:each) do @define = stub_everything 'define', :arguments => [], :doc => "mydoc", :file => "file", :line => 42 @class = stub_everything 'class' @parser.stubs(:get_class_or_module).returns([@class, "mydef"]) end it "should register a RDoc method to the current container" do @class.expects(:add_method).with { |m| m.name == "mydef"} @parser.document_define("mydef", @define, @class) end it "should attach the documentation to this method" do @class.expects(:add_method).with { |m| m.comment = "mydoc" } @parser.document_define("mydef", @define, @class) end it "should produce a better error message on unhandled exception" do @class.expects(:add_method).raises(ArgumentError) lambda { @parser.document_define("mydef", @define, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end it "should convert all definition parameter to string" do arg = stub 'arg' val = stub 'val' @define.stubs(:arguments).returns({arg => val}) arg.expects(:to_s).returns("arg") val.expects(:to_s).returns("val") @parser.document_define("mydef", @define, @class) end end describe "when documenting nodes" do before :each do @code = stub_everything 'code' @node = stub_everything 'node', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_node = stub_everything 'rdocnode' @class = stub_everything 'class' @class.stubs(:add_node).returns(@rdoc_node) end it "should add a node to the current container" do @class.expects(:add_node).with("mynode", "parent").returns(@rdoc_node) @parser.document_node("mynode", @node, @class) end it "should associate the node documentation to the rdoc node" do @rdoc_node.expects(:comment=).with("mydoc") @parser.document_node("mynode", @node, @class) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for variable definition" do @parser.expects(:scan_for_vardef).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_node, @code) @parser.document_node("mynode", @node, @class) end it "should produce a better error message on unhandled exception" do @class.stubs(:add_node).raises(ArgumentError) lambda { @parser.document_node("mynode", @node, @class) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when documenting classes" do before :each do @code = stub_everything 'code' @class = stub_everything 'class', :doc => "mydoc", :parent => "parent", :code => @code, :file => "file", :line => 42 @rdoc_class = stub_everything 'rdoc-class' @module = stub_everything 'class' @module.stubs(:add_class).returns(@rdoc_class) @parser.stubs(:get_class_or_module).returns([@module, "myclass"]) end it "should add a class to the current container" do @module.expects(:add_class).with(RDoc::PuppetClass, "myclass", "parent").returns(@rdoc_class) @parser.document_class("mynode", @class, @module) end it "should set the superclass" do @rdoc_class.expects(:superclass=).with("parent") @parser.document_class("mynode", @class, @module) end it "should associate the node documentation to the rdoc class" do @rdoc_class.expects(:comment=).with("mydoc") @parser.document_class("mynode", @class, @module) end it "should scan for include and require" do @parser.expects(:scan_for_include_or_require).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should scan for resources if needed" do Puppet.settings.stubs(:[]).with(:document_all).returns(true) @parser.expects(:scan_for_resource).with(@rdoc_class, @code) @parser.document_class("mynode", @class, @module) end it "should produce a better error message on unhandled exception" do @module.stubs(:add_class).raises(ArgumentError) lambda { @parser.document_class("mynode", @class, @module) }.should raise_error(Puppet::ParseError, /in file at line 42/) end end describe "when scanning for includes and requires" do def create_stmt(name) stmt_value = stub "#{name}_value", :to_s => "myclass" Puppet::Parser::AST::Function.new( :name => name, :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_include).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, create_stmt("include")) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt("include") ]) @class.expects(:add_include)#.with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end it "should register requires to the current container" do @code.stubs(:children).returns([ create_stmt("require") ]) @class.expects(:add_require).with { |i| i.is_a?(RDoc::Include) and i.name == "myclass" and i.comment == "mydoc" } @parser.scan_for_include_or_require(@class, [@code]) end end describe "when scanning for realized virtual resources" do def create_stmt stmt_value = stub "resource_ref", :to_s => "File[\"/tmp/a\"]" Puppet::Parser::AST::Function.new( :name => 'realize', :arguments => [stmt_value], :doc => 'mydoc' ) end before(:each) do @class = stub_everything 'class' @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should also scan mono-instruction code" do @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class,create_stmt) end it "should register recursively includes to the current container" do @code.stubs(:children).returns([ create_stmt ]) @class.expects(:add_realize).with { |i| i.is_a?(RDoc::Include) and i.name == "File[\"/tmp/a\"]" and i.comment == "mydoc" } @parser.scan_for_realize(@class, [@code]) end end describe "when scanning for variable definition" do before :each do @class = stub_everything 'class' @stmt = stub_everything 'stmt', :name => "myvar", :value => "myvalue", :doc => "mydoc" @stmt.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(false) @stmt.stubs(:is_a?).with(Puppet::Parser::AST::VarDef).returns(true) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should recursively register variables to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_constant).with { |i| i.is_a?(RDoc::Constant) and i.name == "myvar" and i.comment == "mydoc" } @parser.scan_for_vardef(@class, @stmt) end end describe "when scanning for resources" do before :each do @class = stub_everything 'class' @stmt = Puppet::Parser::AST::Resource.new( :type => "File", :instances => Puppet::Parser::AST::ASTArray.new(:children => [ Puppet::Parser::AST::ResourceInstance.new( :title => Puppet::Parser::AST::Name.new(:value => "myfile"), :parameters => Puppet::Parser::AST::ASTArray.new(:children => []) ) ]), :doc => 'mydoc' ) @code = stub_everything 'code' @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true) end it "should register a PuppetResource to the current container" do @code.stubs(:children).returns([ @stmt ]) @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, [ @code ]) end it "should also scan mono-instruction code" do @class.expects(:add_resource).with { |i| i.is_a?(RDoc::PuppetResource) and i.title == "myfile" and i.comment == "mydoc" } @parser.scan_for_resource(@class, @stmt) end end describe "when parsing plugins" do before :each do @container = stub 'container' end it "should delegate parsing custom facts to parse_facts" do @parser = RDoc::Parser.new(@top_level, "module/manifests/lib/puppet/facter/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_fact).with(@container) @parser.parse_plugins(@container) end it "should delegate parsing plugins to parse_plugins" do @parser = RDoc::Parser.new(@top_level, "module/manifests/lib/puppet/functions/test.rb", nil, Options.instance, RDoc::Stats.new) @parser.expects(:parse_puppet_plugin).with(@container) @parser.parse_plugins(@container) end end describe "when parsing plugins" do before :each do @container = stub_everything 'container' end it "should add custom functions to the container" do File.stubs(:open).yields("# documentation module Puppet::Parser::Functions newfunction(:myfunc, :type => :rvalue) do |args| File.dirname(args[0]) end end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "myfunc" end @parser.parse_puppet_plugin(@container) end it "should add custom types to the container" do File.stubs(:open).yields("# documentation Puppet::Type.newtype(:mytype) do end".split("\n")) @container.expects(:add_plugin).with do |plugin| plugin.comment == "documentation\n" #and plugin.name == "mytype" end @parser.parse_puppet_plugin(@container) end end describe "when parsing facts" do before :each do @container = stub_everything 'container' File.stubs(:open).yields(["# documentation", "Facter.add('myfact') do", "confine :kernel => :linux", "end"]) end it "should add facts to the container" do @container.expects(:add_fact).with do |fact| fact.comment == "documentation\n" and fact.name == "myfact" end @parser.parse_fact(@container) end it "should add confine to the parsed facts" do ourfact = nil @container.expects(:add_fact).with do |fact| ourfact = fact true end @parser.parse_fact(@container) ourfact.confine.should == { :type => "kernel", :value => ":linux" } end end end diff --git a/spec/unit/util/run_mode_spec.rb b/spec/unit/util/run_mode_spec.rb index c8d2b31f6..883ee1206 100755 --- a/spec/unit/util/run_mode_spec.rb +++ b/spec/unit/util/run_mode_spec.rb @@ -1,50 +1,50 @@ #!/usr/bin/env rspec require 'spec_helper' -describe Puppet::Util::RunMode do +describe Puppet::Util::RunMode, :fails_on_windows => true do before do @run_mode = Puppet::Util::RunMode.new('fake') end it "should have confdir /etc/puppet when run as root" do Puppet.features.stubs(:root?).returns(true) @run_mode.conf_dir.should == '/etc/puppet' end it "should have confdir ~/.puppet when run as non-root" do Puppet.features.stubs(:root?).returns(false) @run_mode.expects(:expand_path).with("~/.puppet").returns("~/.puppet") @run_mode.conf_dir.should == "~/.puppet" end it "should have vardir /var/lib/puppet when run as root" do Puppet.features.stubs(:root?).returns(true) @run_mode.var_dir.should == '/var/lib/puppet' end it "should have vardir ~/.puppet/var when run as non-root" do Puppet.features.stubs(:root?).returns(false) @run_mode.expects(:expand_path).with("~/.puppet/var").returns("~/.puppet/var") @run_mode.var_dir.should == "~/.puppet/var" end it "should have rundir depend on vardir" do @run_mode.run_dir.should == '$vardir/run' end it "should have logopts return an array with $vardir/log if runmode is not master" do @run_mode.expects(:master?).returns false @run_mode.logopts.should == ["$vardir/log", "The Puppet log directory."] end it "should have logopts return a hash with $vardir/log and other metadata if runmode is master" do @run_mode.expects(:master?).returns true @run_mode.logopts.should == { :default => "$vardir/log", :mode => 0750, :owner => "service", :group => "service", :desc => "The Puppet log directory.", } end end diff --git a/spec/unit/util/settings/file_setting_spec.rb b/spec/unit/util/settings/file_setting_spec.rb index 489628a78..01d891f08 100755 --- a/spec/unit/util/settings/file_setting_spec.rb +++ b/spec/unit/util/settings/file_setting_spec.rb @@ -1,257 +1,262 @@ #!/usr/bin/env rspec require 'spec_helper' require 'puppet/util/settings' require 'puppet/util/settings/file_setting' describe Puppet::Util::Settings::FileSetting do FileSetting = Puppet::Util::Settings::FileSetting + include PuppetSpec::Files + before do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + @basepath = make_absolute("/somepath") end describe "when determining whether the service user should be used" do before do @settings = mock 'settings' @settings.stubs(:[]).with(:mkusers).returns false @settings.stubs(:service_user_available?).returns true end it "should be true if the service user is available" do @settings.expects(:service_user_available?).returns true setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end it "should be true if 'mkusers' is set" do @settings.expects(:[]).with(:mkusers).returns true setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end it "should be false if the service user is not available and 'mkusers' is unset" do setting = FileSetting.new(:settings => @settings, :owner => "root", :desc => "a setting") setting.should be_use_service_user end end describe "when setting the owner" do it "should allow the file to be owned by root" do root_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") } root_owner.should_not raise_error end it "should allow the file to be owned by the service user" do service_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "service", :desc => "a setting") } service_owner.should_not raise_error end it "should allow the ownership of the file to be unspecified" do no_owner = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } no_owner.should_not raise_error end it "should not allow other owners" do invalid_owner = lambda { FileSetting.new(:settings => mock("settings"), :owner => "invalid", :desc => "a setting") } invalid_owner.should raise_error(FileSetting::SettingError) end end describe "when reading the owner" do it "should be root when the setting specifies root" do setting = FileSetting.new(:settings => mock("settings"), :owner => "root", :desc => "a setting") setting.owner.should == "root" end it "should be the owner of the service when the setting specifies service and the service user should be used" do settings = mock("settings") settings.stubs(:[]).returns "the_service" setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") setting.expects(:use_service_user?).returns true setting.owner.should == "the_service" end it "should be the root when the setting specifies service and the service user should not be used" do settings = mock("settings") settings.stubs(:[]).returns "the_service" setting = FileSetting.new(:settings => settings, :owner => "service", :desc => "a setting") setting.expects(:use_service_user?).returns false setting.owner.should == "root" end it "should be nil when the owner is unspecified" do FileSetting.new(:settings => mock("settings"), :desc => "a setting").owner.should be_nil end end describe "when setting the group" do it "should allow the group to be service" do service_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "service", :desc => "a setting") } service_group.should_not raise_error end it "should allow the group to be unspecified" do no_group = lambda { FileSetting.new(:settings => mock("settings"), :desc => "a setting") } no_group.should_not raise_error end it "should not allow invalid groups" do invalid_group = lambda { FileSetting.new(:settings => mock("settings"), :group => "invalid", :desc => "a setting") } invalid_group.should raise_error(FileSetting::SettingError) end end describe "when reading the group" do it "should be service when the setting specifies service" do setting = FileSetting.new(:settings => mock("settings", :[] => "the_service"), :group => "service", :desc => "a setting") setting.group.should == "the_service" end it "should be nil when the group is unspecified" do FileSetting.new(:settings => mock("settings"), :desc => "a setting").group.should be_nil end end it "should be able to be converted into a resource" do FileSetting.new(:settings => mock("settings"), :desc => "eh").should respond_to(:to_resource) end describe "when being converted to a resource" do before do @settings = mock 'settings' @file = Puppet::Util::Settings::FileSetting.new(:settings => @settings, :desc => "eh", :name => :mydir, :section => "mysect") @settings.stubs(:value).with(:mydir).returns @basepath end it "should skip files that cannot determine their types" do @file.expects(:type).returns nil @file.to_resource.should be_nil end it "should skip non-existent files if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file File.expects(:exist?).with(@basepath).returns false @file.to_resource.should be_nil end it "should manage existent files even if 'create_files' is not enabled" do @file.expects(:create_files?).returns false @file.expects(:type).returns :file File.expects(:exist?).with(@basepath).returns true @file.to_resource.should be_instance_of(Puppet::Resource) end describe "on POSIX systems", :if => Puppet.features.posix? do it "should skip files in /dev" do @settings.stubs(:value).with(:mydir).returns "/dev/file" @file.to_resource.should be_nil end end it "should skip files whose paths are not strings" do @settings.stubs(:value).with(:mydir).returns :foo @file.to_resource.should be_nil end it "should return a file resource with the path set appropriately" do resource = @file.to_resource resource.type.should == "File" resource.title.should == @basepath end it "should fully qualified returned files if necessary (#795)" do @settings.stubs(:value).with(:mydir).returns "myfile" - @file.to_resource.title.should == File.join(Dir.getwd, "myfile") + path = File.join(Dir.getwd, "myfile") + # Dir.getwd can return windows paths with backslashes, so we normalize them using expand_path + path = File.expand_path(path) if Puppet.features.microsoft_windows? + @file.to_resource.title.should == path end it "should set the mode on the file if a mode is provided" do @file.mode = 0755 @file.to_resource[:mode].should == 0755 end it "should not set the mode on a the file if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false @file.stubs(:mode).returns(0755) @file.to_resource[:mode].should == nil end it "should set the owner if running as root and the owner is provided" do Puppet.features.expects(:root?).returns true @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == "foo" end it "should not set the owner if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false Puppet.features.stubs(:root?).returns true @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should == nil end it "should set the group if running as root and the group is provided" do Puppet.features.expects(:root?).returns true @file.stubs(:group).returns "foo" @file.to_resource[:group].should == "foo" end it "should not set the group if manage_internal_file_permissions is disabled" do Puppet[:manage_internal_file_permissions] = false Puppet.features.stubs(:root?).returns true @file.stubs(:group).returns "foo" @file.to_resource[:group].should == nil end it "should not set owner if not running as root" do Puppet.features.expects(:root?).returns false @file.stubs(:owner).returns "foo" @file.to_resource[:owner].should be_nil end it "should not set group if not running as root" do Puppet.features.expects(:root?).returns false @file.stubs(:group).returns "foo" @file.to_resource[:group].should be_nil end it "should set :ensure to the file type" do @file.expects(:type).returns :directory @file.to_resource[:ensure].should == :directory end it "should set the loglevel to :debug" do @file.to_resource[:loglevel].should == :debug end it "should set the backup to false" do @file.to_resource[:backup].should be_false end it "should tag the resource with the settings section" do @file.expects(:section).returns "mysect" @file.to_resource.should be_tagged("mysect") end it "should tag the resource with the setting name" do @file.to_resource.should be_tagged("mydir") end it "should tag the resource with 'settings'" do @file.to_resource.should be_tagged("settings") end it "should set links to 'follow'" do @file.to_resource[:links].should == :follow end end end diff --git a/spec/unit/util/settings_spec.rb b/spec/unit/util/settings_spec.rb index aa50c8f3a..efe2be443 100755 --- a/spec/unit/util/settings_spec.rb +++ b/spec/unit/util/settings_spec.rb @@ -1,1107 +1,1111 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Util::Settings do + include PuppetSpec::Files + describe "when specifying defaults" do before do @settings = Puppet::Util::Settings.new end it "should start with no defined parameters" do @settings.params.length.should == 0 end it "should allow specification of default values associated with a section as an array" do @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) end it "should not allow duplicate parameter specifications" do @settings.setdefaults(:section, :myvalue => ["a", "b"]) lambda { @settings.setdefaults(:section, :myvalue => ["c", "d"]) }.should raise_error(ArgumentError) end it "should allow specification of default values associated with a section as a hash" do @settings.setdefaults(:section, :myvalue => {:default => "defaultval", :desc => "my description"}) end it "should consider defined parameters to be valid" do @settings.setdefaults(:section, :myvalue => ["defaultval", "my description"]) @settings.valid?(:myvalue).should be_true end it "should require a description when defaults are specified with an array" do lambda { @settings.setdefaults(:section, :myvalue => ["a value"]) }.should raise_error(ArgumentError) end it "should require a description when defaults are specified with a hash" do lambda { @settings.setdefaults(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError) end it "should raise an error if we can't guess the type" do lambda { @settings.setdefaults(:section, :myvalue => {:default => Object.new, :desc => "An impossible object"}) }.should raise_error(ArgumentError) end it "should support specifying owner, group, and mode when specifying files" do @settings.setdefaults(:section, :myvalue => {:default => "/some/file", :owner => "service", :mode => "boo", :group => "service", :desc => "whatever"}) end it "should support specifying a short name" do @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) end it "should support specifying the setting type" do @settings.setdefaults(:section, :myvalue => {:default => "/w", :desc => "b", :type => :setting}) @settings.setting(:myvalue).should be_instance_of(Puppet::Util::Settings::Setting) end it "should fail if an invalid setting type is specified" do lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :type => :foo}) }.should raise_error(ArgumentError) end it "should fail when short names conflict" do @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) lambda { @settings.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError) end end describe "when setting values" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :main, :myval => ["val", "desc"] @settings.setdefaults :main, :bool => [true, "desc"] end it "should provide a method for setting values from other objects" do @settings[:myval] = "something else" @settings[:myval].should == "something else" end it "should support a getopt-specific mechanism for setting values" do @settings.handlearg("--myval", "newval") @settings[:myval].should == "newval" end it "should support a getopt-specific mechanism for turning booleans off" do @settings[:bool] = true @settings.handlearg("--no-bool", "") @settings[:bool].should == false end it "should support a getopt-specific mechanism for turning booleans on" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with no argument to be a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool") @settings[:bool].should == true end it "should consider a cli setting with an empty string as an argument to be a boolean, if the setting itself is a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "") @settings[:bool].should == true end it "should consider a cli setting with an empty string as an argument to be an empty argument, if the setting itself is not a boolean" do @settings[:myval] = "bob" @settings.handlearg("--myval", "") @settings[:myval].should == "" end it "should consider a cli setting with a boolean as an argument to be a boolean" do # Turn it off first @settings[:bool] = false @settings.handlearg("--bool", "true") @settings[:bool].should == true end it "should not consider a cli setting of a non boolean with a boolean as an argument to be a boolean" do # Turn it off first @settings[:myval] = "bob" @settings.handlearg("--no-myval", "") @settings[:myval].should == "" end it "should clear the cache when setting getopt-specific values" do @settings.setdefaults :mysection, :one => ["whah", "yay"], :two => ["$one yay", "bah"] @settings[:two].should == "whah yay" @settings.handlearg("--one", "else") @settings[:two].should == "else yay" end it "should not clear other values when setting getopt-specific values" do @settings[:myval] = "yay" @settings.handlearg("--no-bool", "") @settings[:myval].should == "yay" end it "should clear the list of used sections" do @settings.expects(:clearused) @settings[:myval] = "yay" end it "should call passed blocks when values are set" do values = [] @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings[:hooker] = "something" values.should == %w{something} end it "should call passed blocks when values are set via the command line" do values = [] @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }}) values.should == [] @settings.handlearg("--hooker", "yay") values.should == %w{yay} end it "should provide an option to call passed blocks during definition" do values = [] @settings.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) values.should == %w{yay} end it "should pass the fully interpolated value to the hook when called on definition" do values = [] @settings.setdefaults(:section, :one => ["test", "a"]) @settings.setdefaults(:section, :hooker => {:default => "$one/yay", :desc => "boo", :call_on_define => true, :hook => lambda { |v| values << v }}) values.should == %w{test/yay} end it "should munge values using the setting-specific methods" do @settings[:bool] = "false" @settings[:bool].should == false end it "should prefer cli values to values set in Ruby code" do @settings.handlearg("--myval", "cliarg") @settings[:myval] = "memarg" @settings[:myval].should == "cliarg" end it "should clear the list of environments" do Puppet::Node::Environment.expects(:clear).at_least(1) @settings[:myval] = "memarg" end it "should raise an error if we try to set 'name'" do lambda{ @settings[:name] = "foo" }.should raise_error(ArgumentError) end it "should raise an error if we try to set 'run_mode'" do lambda{ @settings[:run_mode] = "foo" }.should raise_error(ArgumentError) end it "should warn and use [master] if we ask for [puppetmasterd]" do Puppet.expects(:warning) @settings.set_value(:myval, "foo", :puppetmasterd) @settings.stubs(:run_mode).returns(:master) @settings.value(:myval).should == "foo" end it "should warn and use [agent] if we ask for [puppetd]" do Puppet.expects(:warning) @settings.set_value(:myval, "foo", :puppetd) @settings.stubs(:run_mode).returns(:agent) @settings.value(:myval).should == "foo" end end describe "when returning values" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :section, :config => ["/my/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"], :four => ["$two $three FOUR", "d"] FileTest.stubs(:exist?).returns true end it "should provide a mechanism for returning set values" do @settings[:one] = "other" @settings[:one].should == "other" end it "should interpolate default values for other parameters into returned parameter values" do @settings[:one].should == "ONE" @settings[:two].should == "ONE TWO" @settings[:three].should == "ONE ONE TWO THREE" end it "should interpolate default values that themselves need to be interpolated" do @settings[:four].should == "ONE TWO ONE ONE TWO THREE FOUR" end it "should provide a method for returning uninterpolated values" do @settings[:two] = "$one tw0" @settings.uninterpolated_value(:two).should == "$one tw0" @settings.uninterpolated_value(:four).should == "$two $three FOUR" end it "should interpolate set values for other parameters into returned parameter values" do @settings[:one] = "on3" @settings[:two] = "$one tw0" @settings[:three] = "$one $two thr33" @settings[:four] = "$one $two $three f0ur" @settings[:one].should == "on3" @settings[:two].should == "on3 tw0" @settings[:three].should == "on3 on3 tw0 thr33" @settings[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur" end it "should not cache interpolated values such that stale information is returned" do @settings[:two].should == "ONE TWO" @settings[:one] = "one" @settings[:two].should == "one TWO" end it "should not cache values such that information from one environment is returned for another environment" do text = "[env1]\none = oneval\n[env2]\none = twoval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:one, "env1").should == "oneval" @settings.value(:one, "env2").should == "twoval" end it "should have a run_mode that defaults to user" do @settings.run_mode.should == :user end end describe "when choosing which value to return" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :section, :config => ["/my/file", "a"], :one => ["ONE", "a"], :two => ["TWO", "b"] FileTest.stubs(:exist?).returns true Puppet.stubs(:run_mode).returns stub('run_mode', :name => :mymode) end it "should return default values if no values have been set" do @settings[:one].should == "ONE" end it "should return values set on the cli before values set in the configuration file" do text = "[main]\none = fileval\n" @settings.stubs(:read_file).returns(text) @settings.handlearg("--one", "clival") @settings.parse @settings[:one].should == "clival" end it "should return values set on the cli before values set in Ruby" do @settings[:one] = "rubyval" @settings.handlearg("--one", "clival") @settings[:one].should == "clival" end it "should return values set in the mode-specific section before values set in the main section" do text = "[main]\none = mainval\n[mymode]\none = modeval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings[:one].should == "modeval" end it "should not return values outside of its search path" do text = "[other]\none = oval\n" file = "/some/file" @settings.stubs(:read_file).returns(text) @settings.parse @settings[:one].should == "ONE" end it "should return values in a specified environment" do text = "[env]\none = envval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:one, "env").should == "envval" end it 'should use the current environment for $environment' do @settings.setdefaults :main, :myval => ["$environment/foo", "mydocs"] @settings.value(:myval, "myenv").should == "myenv/foo" end it "should interpolate found values using the current environment" do text = "[main]\none = mainval\n[myname]\none = nameval\ntwo = $one/two\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:two, "myname").should == "nameval/two" end it "should return values in a specified environment before values in the main or name sections" do text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings.value(:one, "env").should == "envval" end end describe "when parsing its configuration" do before do @settings = Puppet::Util::Settings.new @settings.stubs(:service_user_available?).returns true @file = "/some/file" @settings.setdefaults :section, :user => ["suser", "doc"], :group => ["sgroup", "doc"] @settings.setdefaults :section, :config => ["/some/file", "eh"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] FileTest.stubs(:exist?).returns true end it "should not ignore the report setting" do @settings.setdefaults :section, :report => ["false", "a"] myfile = stub "myfile" @settings[:config] = myfile text = <<-CONF [puppetd] report=true CONF @settings.expects(:read_file).returns(text) @settings.parse @settings[:report].should be_true end it "should use its current ':config' value for the file to parse" do - myfile = Puppet.features.posix? ? "/my/file" : "C:/myfile" # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it + myfile = make_absolute("/my/file") # do not stub expand_path here, as this leads to a stack overflow, when mocha tries to use it @settings[:config] = myfile File.expects(:read).with(myfile).returns "[main]" @settings.parse end it "should fail if no configuration setting is defined" do @settings = Puppet::Util::Settings.new lambda { @settings.parse }.should raise_error(RuntimeError) end it "should not try to parse non-existent files" do FileTest.expects(:exist?).with("/some/file").returns false File.expects(:read).with("/some/file").never @settings.parse end it "should set a timer that triggers reparsing, even if the file does not exist" do FileTest.expects(:exist?).returns false @settings.expects(:set_filetimeout_timer) @settings.parse end it "should return values set in the configuration file" do text = "[main] one = fileval " @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == "fileval" end #484 - this should probably be in the regression area it "should not throw an exception on unknown parameters" do text = "[main]\nnosuchparam = mval\n" @settings.expects(:read_file).returns(text) lambda { @settings.parse }.should_not raise_error end it "should convert booleans in the configuration file into Ruby booleans" do text = "[main] one = true two = false " @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == true @settings[:two].should == false end it "should convert integers in the configuration file into Ruby Integers" do text = "[main] one = 65 " @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == 65 end it "should support specifying all metadata (owner, group, mode) in the configuration file" do @settings.setdefaults :section, :myfile => ["/myfile", "a"] + otherfile = make_absolute("/other/file") text = "[main] - myfile = /other/file {owner = service, group = service, mode = 644} + myfile = #{otherfile} {owner = service, group = service, mode = 644} " @settings.expects(:read_file).returns(text) @settings.parse - @settings[:myfile].should == "/other/file" + @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser", :group => "sgroup", :mode => "644"} end it "should support specifying a single piece of metadata (owner, group, or mode) in the configuration file" do @settings.setdefaults :section, :myfile => ["/myfile", "a"] + otherfile = make_absolute("/other/file") text = "[main] - myfile = /other/file {owner = service} + myfile = #{otherfile} {owner = service} " file = "/some/file" @settings.expects(:read_file).returns(text) @settings.parse - @settings[:myfile].should == "/other/file" + @settings[:myfile].should == otherfile @settings.metadata(:myfile).should == {:owner => "suser"} end it "should call hooks associated with values set in the configuration file" do values = [] @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = setval " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["setval"] end it "should not call the same hook for values set multiple times in the configuration file" do values = [] @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[user] mysetting = setval [main] mysetting = other " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["setval"] end it "should pass the environment-specific value to the hook when one is available" do values = [] @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} @settings.setdefaults :section, :environment => ["yay", "a"] @settings.setdefaults :section, :environments => ["yay,foo", "a"] text = "[main] mysetting = setval [yay] mysetting = other " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["other"] end it "should pass the interpolated value to the hook when one is available" do values = [] @settings.setdefaults :section, :base => {:default => "yay", :desc => "a", :hook => proc { |v| values << v }} @settings.setdefaults :section, :mysetting => {:default => "defval", :desc => "a", :hook => proc { |v| values << v }} text = "[main] mysetting = $base/setval " @settings.expects(:read_file).returns(text) @settings.parse values.should == ["yay/setval"] end it "should allow empty values" do @settings.setdefaults :section, :myarg => ["myfile", "a"] text = "[main] myarg = " @settings.stubs(:read_file).returns(text) @settings.parse @settings[:myarg].should == "" end describe "and when reading a non-positive filetimeout value from the config file" do before do @settings.setdefaults :foo, :filetimeout => [5, "eh"] somefile = "/some/file" text = "[main] filetimeout = -1 " File.expects(:read).with(somefile).returns(text) File.expects(:expand_path).with(somefile).returns somefile @settings[:config] = somefile end it "should not set a timer" do EventLoop::Timer.expects(:new).never @settings.parse end end end describe "when reparsing its configuration" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :section, :config => ["/test/file", "a"], :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"] FileTest.stubs(:exist?).returns true end it "should use a LoadedFile instance to determine if the file has changed" do file = mock 'file' Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file file.expects(:changed?) @settings.stubs(:parse) @settings.reparse end it "should not create the LoadedFile instance and should not parse if the file does not exist" do FileTest.expects(:exist?).with("/test/file").returns false Puppet::Util::LoadedFile.expects(:new).never @settings.expects(:parse).never @settings.reparse end it "should not reparse if the file has not changed" do file = mock 'file' Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file file.expects(:changed?).returns false @settings.expects(:parse).never @settings.reparse end it "should reparse if the file has changed" do file = stub 'file', :file => "/test/file" Puppet::Util::LoadedFile.expects(:new).with("/test/file").returns file file.expects(:changed?).returns true @settings.expects(:parse) @settings.reparse end it "should use a cached LoadedFile instance" do first = mock 'first' second = mock 'second' Puppet::Util::LoadedFile.expects(:new).times(2).with("/test/file").returns(first).then.returns(second) @settings.file.should equal(first) Puppet::Util::Cacher.expire @settings.file.should equal(second) end it "should replace in-memory values with on-file values" do # Init the value text = "[main]\none = disk-init\n" file = mock 'file' file.stubs(:changed?).returns(true) file.stubs(:file).returns("/test/file") @settings[:one] = "init" @settings.file = file # Now replace the value text = "[main]\none = disk-replace\n" # This is kinda ridiculous - the reason it parses twice is that # it goes to parse again when we ask for the value, because the # mock always says it should get reparsed. @settings.stubs(:read_file).returns(text) @settings.reparse @settings[:one].should == "disk-replace" end it "should retain parameters set by cli when configuration files are reparsed" do @settings.handlearg("--one", "clival") text = "[main]\none = on-disk\n" @settings.stubs(:read_file).returns(text) @settings.parse @settings[:one].should == "clival" end it "should remove in-memory values that are no longer set in the file" do # Init the value text = "[main]\none = disk-init\n" @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == "disk-init" # Now replace the value text = "[main]\ntwo = disk-replace\n" @settings.expects(:read_file).returns(text) @settings.parse #@settings.reparse # The originally-overridden value should be replaced with the default @settings[:one].should == "ONE" # and we should now have the new value in memory @settings[:two].should == "disk-replace" end it "should retain in-memory values if the file has a syntax error" do # Init the value text = "[main]\none = initial-value\n" @settings.expects(:read_file).returns(text) @settings.parse @settings[:one].should == "initial-value" # Now replace the value with something bogus text = "[main]\nkenny = killed-by-what-follows\n1 is 2, blah blah florp\n" @settings.expects(:read_file).returns(text) @settings.parse # The originally-overridden value should not be replaced with the default @settings[:one].should == "initial-value" # and we should not have the new value in memory @settings[:kenny].should be_nil end end it "should provide a method for creating a catalog of resources from its configuration" do Puppet::Util::Settings.new.should respond_to(:to_catalog) end describe "when creating a catalog" do before do @settings = Puppet::Util::Settings.new @settings.stubs(:service_user_available?).returns true @prefix = Puppet.features.posix? ? "" : "C:" end it "should add all file resources to the catalog if no sections have been specified" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"], :seconddir => [@prefix+"/seconddir", "a"] @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] catalog = @settings.to_catalog [@prefix+"/maindir", @prefix+"/seconddir", @prefix+"/otherdir"].each do |path| catalog.resource(:file, path).should be_instance_of(Puppet::Resource) end end it "should add only files in the specified sections if section names are provided" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] @settings.setdefaults :other, :otherdir => [@prefix+"/otherdir", "a"] catalog = @settings.to_catalog(:main) catalog.resource(:file, @prefix+"/otherdir").should be_nil catalog.resource(:file, @prefix+"/maindir").should be_instance_of(Puppet::Resource) end it "should not try to add the same file twice" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] @settings.setdefaults :other, :otherdir => [@prefix+"/maindir", "a"] lambda { @settings.to_catalog }.should_not raise_error end it "should ignore files whose :to_resource method returns nil" do @settings.setdefaults :main, :maindir => [@prefix+"/maindir", "a"] @settings.setting(:maindir).expects(:to_resource).returns nil Puppet::Resource::Catalog.any_instance.expects(:add_resource).never @settings.to_catalog end describe "when adding users and groups to the catalog" do before do Puppet.features.stubs(:root?).returns true @settings.setdefaults :foo, :mkusers => [true, "e"], :user => ["suser", "doc"], :group => ["sgroup", "doc"] @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} @catalog = @settings.to_catalog end it "should add each specified user and group to the catalog if :mkusers is a valid setting, is enabled, and we're running as root" do @catalog.resource(:user, "suser").should be_instance_of(Puppet::Resource) @catalog.resource(:group, "sgroup").should be_instance_of(Puppet::Resource) end it "should only add users and groups to the catalog from specified sections" do @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} catalog = @settings.to_catalog(:other) catalog.resource(:user, "jane").should be_nil catalog.resource(:group, "billy").should be_nil end it "should not add users or groups to the catalog if :mkusers not running as root" do Puppet.features.stubs(:root?).returns false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is not a valid setting" do Puppet.features.stubs(:root?).returns true settings = Puppet::Util::Settings.new settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service"} catalog = settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not add users or groups to the catalog if :mkusers is a valid setting but is disabled" do @settings[:mkusers] = false catalog = @settings.to_catalog catalog.resource(:user, "suser").should be_nil catalog.resource(:group, "sgroup").should be_nil end it "should not try to add users or groups to the catalog twice" do @settings.setdefaults :yay, :yaydir => {:default => "/yaydir", :desc => "a", :owner => "service", :group => "service"} # This would fail if users/groups were added twice lambda { @settings.to_catalog }.should_not raise_error end it "should set :ensure to :present on each created user and group" do @catalog.resource(:user, "suser")[:ensure].should == :present @catalog.resource(:group, "sgroup")[:ensure].should == :present end it "should set each created user's :gid to the service group" do @settings.to_catalog.resource(:user, "suser")[:gid].should == "sgroup" end it "should not attempt to manage the root user" do Puppet.features.stubs(:root?).returns true @settings.setdefaults :foo, :foodir => {:default => "/foodir", :desc => "a", :owner => "root", :group => "service"} @settings.to_catalog.resource(:user, "root").should be_nil end end end it "should be able to be converted to a manifest" do Puppet::Util::Settings.new.should respond_to(:to_manifest) end describe "when being converted to a manifest" do it "should produce a string with the code for each resource joined by two carriage returns" do @settings = Puppet::Util::Settings.new @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] main = stub 'main_resource', :ref => "File[/maindir]" main.expects(:to_manifest).returns "maindir" second = stub 'second_resource', :ref => "File[/seconddir]" second.expects(:to_manifest).returns "seconddir" @settings.setting(:maindir).expects(:to_resource).returns main @settings.setting(:seconddir).expects(:to_resource).returns second @settings.to_manifest.split("\n\n").sort.should == %w{maindir seconddir} end end describe "when using sections of the configuration to manage the local host" do before do @settings = Puppet::Util::Settings.new @settings.stubs(:service_user_available?).returns true @settings.setdefaults :main, :noop => [false, ""] @settings.setdefaults :main, :maindir => ["/maindir", "a"], :seconddir => ["/seconddir", "a"] @settings.setdefaults :main, :user => ["suser", "doc"], :group => ["sgroup", "doc"] @settings.setdefaults :other, :otherdir => {:default => "/otherdir", :desc => "a", :owner => "service", :group => "service", :mode => 0755} @settings.setdefaults :third, :thirddir => ["/thirddir", "b"] @settings.setdefaults :files, :myfile => {:default => "/myfile", :desc => "a", :mode => 0755} end it "should provide a method that writes files with the correct modes" do @settings.should respond_to(:write) end it "should provide a method that creates directories with the correct modes" do Puppet::Util::SUIDManager.expects(:asuser).with("suser", "sgroup").yields Dir.expects(:mkdir).with("/otherdir", 0755) @settings.mkdir(:otherdir) end it "should create a catalog with the specified sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should canonicalize the sections" do @settings.expects(:to_catalog).with(:main, :other).returns Puppet::Resource::Catalog.new("foo") @settings.use("main", "other") end it "should ignore sections that have already been used" do @settings.expects(:to_catalog).with(:main).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main) @settings.expects(:to_catalog).with(:other).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :other) end it "should ignore tags and schedules when creating files and directories" it "should be able to provide all of its parameters in a format compatible with GetOpt::Long" do pending "Not converted from test/unit yet" end it "should convert the created catalog to a RAL catalog" do @catalog = Puppet::Resource::Catalog.new("foo") @settings.expects(:to_catalog).with(:main).returns @catalog @catalog.expects(:to_ral).returns @catalog @settings.use(:main) end it "should specify that it is not managing a host catalog" do catalog = Puppet::Resource::Catalog.new("foo") catalog.expects(:apply) @settings.expects(:to_catalog).returns catalog catalog.stubs(:to_ral).returns catalog catalog.expects(:host_config=).with false @settings.use(:main) end it "should support a method for re-using all currently used sections" do @settings.expects(:to_catalog).with(:main, :third).times(2).returns Puppet::Resource::Catalog.new("foo") @settings.use(:main, :third) @settings.reuse end it "should fail with an appropriate message if any resources fail" do @catalog = Puppet::Resource::Catalog.new("foo") @catalog.stubs(:to_ral).returns @catalog @settings.expects(:to_catalog).returns @catalog @trans = mock("transaction") @catalog.expects(:apply).yields(@trans) @trans.expects(:any_failed?).returns(true) report = mock 'report' @trans.expects(:report).returns report log = mock 'log', :to_s => "My failure", :level => :err report.expects(:logs).returns [log] @settings.expects(:raise).with { |msg| msg.include?("My failure") } @settings.use(:whatever) end end describe "when dealing with printing configs" do before do @settings = Puppet::Util::Settings.new #these are the magic default values @settings.stubs(:value).with(:configprint).returns("") @settings.stubs(:value).with(:genconfig).returns(false) @settings.stubs(:value).with(:genmanifest).returns(false) @settings.stubs(:value).with(:environment).returns(nil) end describe "when checking print_config?" do it "should return false when the :configprint, :genconfig and :genmanifest are not set" do @settings.print_configs?.should be_false end it "should return true when :configprint has a value" do @settings.stubs(:value).with(:configprint).returns("something") @settings.print_configs?.should be_true end it "should return true when :genconfig has a value" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.print_configs?.should be_true end it "should return true when :genmanifest has a value" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.print_configs?.should be_true end end describe "when printing configs" do describe "when :configprint has a value" do it "should call print_config_options" do @settings.stubs(:value).with(:configprint).returns("something") @settings.expects(:print_config_options) @settings.print_configs end it "should get the value of the option using the environment" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.expects(:value).with(:environment).returns("env") @settings.expects(:value).with("something", "env").returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs end it "should print the value of the option" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.expects(:puts).with("foo") @settings.print_configs end it "should print the value pairs if there are multiple options" do @settings.stubs(:value).with(:configprint).returns("bar,baz") @settings.stubs(:include?).with("bar").returns(true) @settings.stubs(:include?).with("baz").returns(true) @settings.stubs(:value).with("bar", nil).returns("foo") @settings.stubs(:value).with("baz", nil).returns("fud") @settings.expects(:puts).with("bar = foo") @settings.expects(:puts).with("baz = fud") @settings.print_configs end it "should print a whole bunch of stuff if :configprint = all" it "should return true after printing" do @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(true) @settings.stubs(:value).with("something", nil).returns("foo") @settings.stubs(:puts).with("foo") @settings.print_configs.should be_true end it "should return false if a config param is not found" do @settings.stubs :puts @settings.stubs(:value).with(:configprint).returns("something") @settings.stubs(:include?).with("something").returns(false) @settings.print_configs.should be_false end end describe "when genconfig is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.expects(:to_config) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genconfig).returns(true) @settings.stubs(:to_config) @settings.print_configs.should be_true end end describe "when genmanifest is true" do before do @settings.stubs :puts end it "should call to_config" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.expects(:to_manifest) @settings.print_configs end it "should return true from print_configs" do @settings.stubs(:value).with(:genmanifest).returns(true) @settings.stubs(:to_manifest) @settings.print_configs.should be_true end end end end describe "when setting a timer to trigger configuration file reparsing" do before do @settings = Puppet::Util::Settings.new @settings.setdefaults :foo, :filetimeout => [5, "eh"] end it "should do nothing if no filetimeout setting is available" do @settings.expects(:value).with(:filetimeout).returns nil EventLoop::Timer.expects(:new).never @settings.set_filetimeout_timer end it "should always convert the timer interval to an integer" do @settings.expects(:value).with(:filetimeout).returns "10" EventLoop::Timer.expects(:new).with(:interval => 10, :start? => true, :tolerance => 1) @settings.set_filetimeout_timer end it "should do nothing if the filetimeout setting is not greater than 0" do @settings.expects(:value).with(:filetimeout).returns -2 EventLoop::Timer.expects(:new).never @settings.set_filetimeout_timer end it "should create a timer with its interval set to the filetimeout, start? set to true, and a tolerance of 1" do @settings.expects(:value).with(:filetimeout).returns 5 EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1) @settings.set_filetimeout_timer end it "should reparse when the timer goes off" do EventLoop::Timer.expects(:new).with(:interval => 5, :start? => true, :tolerance => 1).yields @settings.expects(:reparse) @settings.set_filetimeout_timer end end describe "when determining if the service user is available" do it "should return false if there is no user setting" do Puppet::Util::Settings.new.should_not be_service_user_available end it "should return false if the user provider says the user is missing" do settings = Puppet::Util::Settings.new settings.setdefaults :main, :user => ["foo", "doc"] user = mock 'user' user.expects(:exists?).returns false Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user settings.should_not be_service_user_available end it "should return true if the user provider says the user is present" do settings = Puppet::Util::Settings.new settings.setdefaults :main, :user => ["foo", "doc"] user = mock 'user' user.expects(:exists?).returns true Puppet::Type.type(:user).expects(:new).with { |args| args[:name] == "foo" }.returns user settings.should be_service_user_available end it "should cache the result" end end diff --git a/spec/unit/util/storage_spec.rb b/spec/unit/util/storage_spec.rb index 90c11aa69..575ad1ef3 100755 --- a/spec/unit/util/storage_spec.rb +++ b/spec/unit/util/storage_spec.rb @@ -1,233 +1,233 @@ #!/usr/bin/env rspec require 'spec_helper' require 'yaml' require 'puppet/util/storage' describe Puppet::Util::Storage do include PuppetSpec::Files before(:all) do - @basepath = Puppet.features.posix? ? "/somepath" : "C:/somepath" + @basepath = make_absolute("/somepath") Puppet[:statedir] = tmpdir("statedir") end after(:all) do Puppet.settings.clear end before(:each) do Puppet::Util::Storage.clear end describe "when caching a symbol" do it "should return an empty hash" do Puppet::Util::Storage.cache(:yayness).should == {} Puppet::Util::Storage.cache(:more_yayness).should == {} end it "should add the symbol to its internal state" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} end it "should not clobber existing state when caching additional objects" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet::Util::Storage.cache(:bubblyness) Puppet::Util::Storage.state.should == {:yayness=>{},:bubblyness=>{}} end end describe "when caching a Puppet::Type" do before(:all) do @file_test = Puppet::Type.type(:file).new(:name => @basepath+"/yayness", :check => %w{checksum type}) @exec_test = Puppet::Type.type(:exec).new(:name => @basepath+"/bin/ls /yayness") end it "should return an empty hash" do Puppet::Util::Storage.cache(@file_test).should == {} Puppet::Util::Storage.cache(@exec_test).should == {} end it "should add the resource ref to its internal state" do Puppet::Util::Storage.state.should == {} Puppet::Util::Storage.cache(@file_test) Puppet::Util::Storage.state.should == {"File[#{@basepath}/yayness]"=>{}} Puppet::Util::Storage.cache(@exec_test) Puppet::Util::Storage.state.should == {"File[#{@basepath}/yayness]"=>{}, "Exec[#{@basepath}/bin/ls /yayness]"=>{}} end end describe "when caching something other than a resource or symbol" do it "should cache by converting to a string" do data = Puppet::Util::Storage.cache(42) data[:yay] = true Puppet::Util::Storage.cache("42")[:yay].should be_true end end it "should clear its internal state when clear() is called" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet::Util::Storage.clear Puppet::Util::Storage.state.should == {} end describe "when loading from the state file" do before do Puppet.settings.stubs(:use).returns(true) end describe "when the state file/directory does not exist" do before(:each) do transient = Tempfile.new('storage_test') @path = transient.path() transient.close!() end it "should not fail to load()" do FileTest.exists?(@path).should be_false Puppet[:statedir] = @path proc { Puppet::Util::Storage.load }.should_not raise_error Puppet[:statefile] = @path proc { Puppet::Util::Storage.load }.should_not raise_error end it "should not lose its internal state when load() is called" do FileTest.exists?(@path).should be_false Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} Puppet[:statefile] = @path proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {:yayness=>{}} end end describe "when the state file/directory exists" do before(:each) do @state_file = Tempfile.new('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file.path end it "should overwrite its internal state if load() is called" do # Should the state be overwritten even if Puppet[:statefile] is not valid YAML? Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {} end it "should restore its internal state if the state file contains valid YAML" do test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == test_yaml end it "should initialize with a clear internal state if the state file does not contain valid YAML" do @state_file.write(:booness) @state_file.flush proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {} end it "should raise an error if the state file does not contain valid YAML and cannot be renamed" do @state_file.write(:booness) @state_file.flush YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).raises(SystemCallError) proc { Puppet::Util::Storage.load }.should raise_error end it "should attempt to rename the state file if the file is corrupted" do # We fake corruption by causing YAML.load to raise an exception YAML.expects(:load).raises(Puppet::Error) File.expects(:rename).at_least_once proc { Puppet::Util::Storage.load }.should_not raise_error end it "should fail gracefully on load() if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) proc { Puppet::Util::Storage.load }.should_not raise_error Dir.rmdir(Puppet[:statefile]) end it "should fail gracefully on load() if it cannot get a read lock on the state file" do Puppet::Util::FileLocking.expects(:readlock).yields(false) test_yaml = {'File["/yayness"]'=>{"name"=>{:a=>:b,:c=>:d}}} YAML.expects(:load).returns(test_yaml) proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == test_yaml end after(:each) do @state_file.close!() Puppet[:statefile] = @saved_statefile end end end describe "when storing to the state file" do before(:each) do @state_file = Tempfile.new('storage_test') @saved_statefile = Puppet[:statefile] Puppet[:statefile] = @state_file.path end it "should create the state file if it does not exist" do @state_file.close!() FileTest.exists?(Puppet[:statefile]).should be_false Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should_not raise_error FileTest.exists?(Puppet[:statefile]).should be_true end it "should raise an exception if the state file is not a regular file" do @state_file.close!() Dir.mkdir(Puppet[:statefile]) Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should raise_error Dir.rmdir(Puppet[:statefile]) end it "should raise an exception if it cannot get a write lock on the state file" do Puppet::Util::FileLocking.expects(:writelock).yields(false) Puppet::Util::Storage.cache(:yayness) proc { Puppet::Util::Storage.store }.should raise_error end it "should load() the same information that it store()s" do Puppet::Util::Storage.cache(:yayness) Puppet::Util::Storage.state.should == {:yayness=>{}} proc { Puppet::Util::Storage.store }.should_not raise_error Puppet::Util::Storage.clear Puppet::Util::Storage.state.should == {} proc { Puppet::Util::Storage.load }.should_not raise_error Puppet::Util::Storage.state.should == {:yayness=>{}} end after(:each) do @state_file.close!() Puppet[:statefile] = @saved_statefile end end end