summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStig Sandbeck Mathisen <ssm@debian.org>2013-12-20 08:40:56 +0100
committerStig Sandbeck Mathisen <ssm@debian.org>2013-12-20 08:40:56 +0100
commit68f6c7efced2e3fe35aefea94ea2cac160cdcd4d (patch)
treeda782122a9c9b4f8d898c664629313c9b303a507
parent90ffd9c9413975b3e6af205b1962364c06128f31 (diff)
parentd0f15cef98a7b77da2bef3c60eb97942b144bd17 (diff)
downloadpuppet-upstream/3.4.0.tar.gz
Imported Upstream version 3.4.0upstream/3.4.0
-rw-r--r--CONTRIBUTING.md22
-rw-r--r--Gemfile40
-rw-r--r--README.md30
-rw-r--r--README_DEVELOPER.md2
-rw-r--r--Rakefile13
-rw-r--r--examples/hiera/README.md8
-rw-r--r--examples/mac_automount.pp16
-rw-r--r--examples/mcx_dock_absent.pp4
-rw-r--r--examples/mcx_dock_default.pp118
-rw-r--r--examples/mcx_dock_full.pp125
-rw-r--r--examples/mcx_dock_invalid.pp9
-rw-r--r--examples/mcx_nogroup.pp118
-rw-r--r--examples/mcx_notexists_absent.pp4
-rw-r--r--ext/build_defaults.yaml4
-rw-r--r--ext/debian/changelog6
-rw-r--r--ext/debian/puppetmaster.init1
-rwxr-xr-xext/debian/rules7
-rw-r--r--ext/ips/puppet.p5m4
-rwxr-xr-xext/nagios/check_puppet.rb14
-rw-r--r--ext/osx/file_mapping.yaml2
-rwxr-xr-xext/osx/preflight.erb53
-rw-r--r--ext/rack/README58
-rw-r--r--ext/rack/config.ru (renamed from ext/rack/files/config.ru)0
-rw-r--r--ext/rack/example-passenger-vhost.conf (renamed from ext/rack/files/apache2.conf)6
-rw-r--r--ext/rack/manifest.pp59
-rw-r--r--ext/redhat/puppet.spec30
-rw-r--r--ext/systemd/puppet.service (renamed from ext/systemd/puppetagent.service)0
-rw-r--r--lib/hiera_puppet.rb4
-rw-r--r--lib/puppet/agent.rb7
-rw-r--r--lib/puppet/application.rb17
-rw-r--r--lib/puppet/application/agent.rb9
-rw-r--r--lib/puppet/application/apply.rb21
-rw-r--r--lib/puppet/application/cert.rb54
-rw-r--r--lib/puppet/application/device.rb7
-rw-r--r--lib/puppet/application/face_base.rb2
-rw-r--r--lib/puppet/application/filebucket.rb2
-rw-r--r--lib/puppet/application/inspect.rb15
-rw-r--r--lib/puppet/application/master.rb7
-rw-r--r--lib/puppet/application/queue.rb7
-rw-r--r--lib/puppet/application/resource.rb8
-rw-r--r--lib/puppet/coercion.rb11
-rw-r--r--lib/puppet/configurer.rb8
-rw-r--r--lib/puppet/configurer/downloader.rb4
-rw-r--r--lib/puppet/configurer/plugin_handler.rb10
-rw-r--r--lib/puppet/confine.rb80
-rw-r--r--lib/puppet/confine/exists.rb (renamed from lib/puppet/provider/confine/exists.rb)6
-rw-r--r--lib/puppet/confine/false.rb (renamed from lib/puppet/provider/confine/false.rb)4
-rw-r--r--lib/puppet/confine/feature.rb (renamed from lib/puppet/provider/confine/feature.rb)4
-rw-r--r--lib/puppet/confine/true.rb (renamed from lib/puppet/provider/confine/true.rb)4
-rw-r--r--lib/puppet/confine/variable.rb (renamed from lib/puppet/provider/confine/variable.rb)4
-rw-r--r--lib/puppet/confine_collection.rb (renamed from lib/puppet/provider/confine_collection.rb)8
-rw-r--r--lib/puppet/confiner.rb (renamed from lib/puppet/provider/confiner.rb)8
-rw-r--r--[-rwxr-xr-x]lib/puppet/daemon.rb8
-rw-r--r--lib/puppet/data_binding.rb32
-rw-r--r--lib/puppet/defaults.rb454
-rw-r--r--lib/puppet/error.rb1
-rw-r--r--lib/puppet/external/lock.rb63
-rw-r--r--[-rwxr-xr-x]lib/puppet/external/nagios.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/external/nagios/base.rb7
-rw-r--r--lib/puppet/external/nagios/grammar.ry285
-rw-r--r--lib/puppet/external/nagios/parser.rb417
-rw-r--r--lib/puppet/external/pson/pure/parser.rb10
-rw-r--r--lib/puppet/face/file/store.rb2
-rw-r--r--lib/puppet/face/module/generate.rb12
-rw-r--r--lib/puppet/face/parser.rb14
-rw-r--r--lib/puppet/face/plugin.rb6
-rw-r--r--lib/puppet/feature/base.rb16
-rw-r--r--lib/puppet/feature/external_facts.rb5
-rw-r--r--lib/puppet/feature/libuser.rb2
-rw-r--r--lib/puppet/feature/msgpack.rb1
-rw-r--r--lib/puppet/feature/rails.rb4
-rw-r--r--lib/puppet/file_bucket/dipper.rb14
-rw-r--r--lib/puppet/file_bucket/file.rb18
-rw-r--r--lib/puppet/file_serving/base.rb31
-rw-r--r--lib/puppet/file_serving/configuration.rb12
-rw-r--r--lib/puppet/file_serving/configuration/parser.rb2
-rw-r--r--lib/puppet/file_serving/content.rb2
-rw-r--r--lib/puppet/file_serving/fileset.rb6
-rw-r--r--lib/puppet/file_serving/metadata.rb40
-rw-r--r--lib/puppet/file_serving/mount/file.rb2
-rw-r--r--lib/puppet/file_serving/mount/pluginfacts.rb35
-rw-r--r--lib/puppet/file_system.rb3
-rw-r--r--lib/puppet/file_system/file.rb261
-rw-r--r--lib/puppet/file_system/file18.rb5
-rw-r--r--lib/puppet/file_system/file19.rb5
-rw-r--r--lib/puppet/file_system/file19windows.rb113
-rw-r--r--lib/puppet/file_system/memory_file.rb31
-rw-r--r--lib/puppet/file_system/tempfile.rb20
-rw-r--r--lib/puppet/indirector/active_record.rb1
-rw-r--r--lib/puppet/indirector/catalog/compiler.rb28
-rw-r--r--lib/puppet/indirector/certificate_request/memory.rb6
-rw-r--r--lib/puppet/indirector/data_binding/hiera.rb48
-rw-r--r--lib/puppet/indirector/direct_file_server.rb4
-rw-r--r--lib/puppet/indirector/facts/facter.rb25
-rw-r--r--lib/puppet/indirector/file_bucket_file/file.rb134
-rw-r--r--lib/puppet/indirector/hiera.rb39
-rw-r--r--lib/puppet/indirector/indirection.rb6
-rw-r--r--lib/puppet/indirector/json.rb2
-rw-r--r--lib/puppet/indirector/key/ca.rb4
-rw-r--r--lib/puppet/indirector/key/file.rb10
-rw-r--r--lib/puppet/indirector/key/memory.rb6
-rw-r--r--lib/puppet/indirector/node/write_only_yaml.rb4
-rw-r--r--lib/puppet/indirector/request.rb28
-rw-r--r--lib/puppet/indirector/resource/ral.rb5
-rw-r--r--lib/puppet/indirector/resource/rest.rb1
-rw-r--r--lib/puppet/indirector/resource/store_configs.rb4
-rw-r--r--lib/puppet/indirector/rest.rb3
-rw-r--r--lib/puppet/indirector/ssl_file.rb14
-rw-r--r--lib/puppet/indirector/terminus.rb4
-rw-r--r--lib/puppet/indirector/yaml.rb6
-rw-r--r--lib/puppet/interface/documentation.rb15
-rw-r--r--lib/puppet/module.rb25
-rw-r--r--lib/puppet/module_tool/applications/builder.rb2
-rw-r--r--lib/puppet/module_tool/applications/installer.rb2
-rw-r--r--lib/puppet/module_tool/checksums.rb2
-rw-r--r--lib/puppet/module_tool/dependency.rb10
-rw-r--r--lib/puppet/module_tool/metadata.rb8
-rw-r--r--lib/puppet/module_tool/tar.rb3
-rw-r--r--lib/puppet/module_tool/tar/gnu.rb8
-rw-r--r--lib/puppet/module_tool/tar/mini.rb2
-rw-r--r--lib/puppet/module_tool/tar/solaris.rb7
-rw-r--r--lib/puppet/network/authconfig.rb2
-rw-r--r--lib/puppet/network/authentication.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/network/authstore.rb13
-rw-r--r--lib/puppet/network/format.rb5
-rw-r--r--lib/puppet/network/format_handler.rb27
-rw-r--r--lib/puppet/network/format_support.rb14
-rw-r--r--lib/puppet/network/formats.rb26
-rw-r--r--lib/puppet/network/http/connection.rb49
-rw-r--r--lib/puppet/network/http/handler.rb60
-rw-r--r--lib/puppet/network/http/webrick.rb37
-rw-r--r--lib/puppet/network/http_pool.rb52
-rw-r--r--[-rwxr-xr-x]lib/puppet/network/rights.rb0
-rw-r--r--lib/puppet/node.rb32
-rw-r--r--lib/puppet/node/environment.rb38
-rw-r--r--[-rwxr-xr-x]lib/puppet/node/facts.rb29
-rw-r--r--lib/puppet/parameter.rb17
-rw-r--r--lib/puppet/parameter/boolean.rb5
-rw-r--r--lib/puppet/parameter/value_collection.rb10
-rw-r--r--lib/puppet/parser/ast/resourceparam.rb3
-rw-r--r--lib/puppet/parser/compiler.rb34
-rw-r--r--lib/puppet/parser/files.rb2
-rw-r--r--lib/puppet/parser/functions.rb33
-rw-r--r--lib/puppet/parser/functions/collect.rb41
-rw-r--r--lib/puppet/parser/functions/contain.rb26
-rw-r--r--lib/puppet/parser/functions/create_resources.rb5
-rw-r--r--lib/puppet/parser/functions/extlookup.rb4
-rw-r--r--lib/puppet/parser/functions/file.rb2
-rw-r--r--lib/puppet/parser/functions/filter.rb (renamed from lib/puppet/parser/functions/reject.rb)25
-rw-r--r--lib/puppet/parser/functions/foreach.rb95
-rw-r--r--lib/puppet/parser/functions/fqdn_rand.rb18
-rw-r--r--lib/puppet/parser/functions/include.rb19
-rw-r--r--lib/puppet/parser/functions/map.rb44
-rw-r--r--lib/puppet/parser/functions/select.rb44
-rw-r--r--lib/puppet/parser/lexer.rb2
-rw-r--r--lib/puppet/parser/parser_support.rb2
-rw-r--r--lib/puppet/parser/resource.rb51
-rw-r--r--lib/puppet/parser/scope.rb35
-rw-r--r--lib/puppet/parser/type_loader.rb64
-rw-r--r--lib/puppet/pops/binder/bindings_loader.rb2
-rw-r--r--lib/puppet/pops/binder/config/binder_config.rb6
-rw-r--r--lib/puppet/pops/binder/hiera2/bindings_provider.rb2
-rw-r--r--lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb2
-rw-r--r--lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb4
-rw-r--r--lib/puppet/pops/issues.rb4
-rw-r--r--lib/puppet/pops/model/ast_transformer.rb5
-rw-r--r--lib/puppet/pops/model/model_label_provider.rb2
-rw-r--r--lib/puppet/pops/parser/egrammar.ra29
-rw-r--r--lib/puppet/pops/parser/eparser.rb1761
-rw-r--r--lib/puppet/pops/parser/lexer.rb78
-rw-r--r--lib/puppet/pops/parser/parser_support.rb2
-rw-r--r--lib/puppet/pops/patterns.rb8
-rw-r--r--lib/puppet/pops/utils.rb2
-rw-r--r--lib/puppet/pops/validation/checker3_1.rb45
-rw-r--r--lib/puppet/provider.rb29
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/aixobject.rb0
-rw-r--r--lib/puppet/provider/augeas/augeas.rb26
-rw-r--r--lib/puppet/provider/confine.rb84
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/cron/crontab.rb0
-rw-r--r--lib/puppet/provider/exec.rb16
-rw-r--r--lib/puppet/provider/exec/posix.rb11
-rw-r--r--lib/puppet/provider/exec/windows.rb2
-rw-r--r--lib/puppet/provider/file/posix.rb1
-rw-r--r--lib/puppet/provider/file/windows.rb21
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/group/aix.rb0
-rw-r--r--lib/puppet/provider/group/windows_adsi.rb34
-rw-r--r--lib/puppet/provider/macauthorization/macauthorization.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/mailalias/aliases.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/maillist/mailman.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/mount/parsed.rb0
-rw-r--r--lib/puppet/provider/nameservice/directoryservice.rb6
-rw-r--r--lib/puppet/provider/package/appdmg.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/apple.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/apt.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/aptitude.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/blastwave.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/dpkg.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/fink.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/freebsd.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/gem.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/macports.rb0
-rw-r--r--lib/puppet/provider/package/msi.rb14
-rw-r--r--lib/puppet/provider/package/nim.rb16
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/openbsd.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/opkg.rb0
-rw-r--r--lib/puppet/provider/package/pacman.rb4
-rw-r--r--lib/puppet/provider/package/pkgdmg.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/pkgutil.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/ports.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/rpm.rb46
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/sun.rb6
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/package/sunfreeware.rb0
-rw-r--r--lib/puppet/provider/package/windows.rb31
-rw-r--r--lib/puppet/provider/package/windows/package.rb2
-rw-r--r--lib/puppet/provider/package/yum.rb4
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/parsedfile.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/port/parsed.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/base.rb0
-rw-r--r--lib/puppet/provider/service/bsd.rb6
-rw-r--r--lib/puppet/provider/service/daemontools.rb16
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/debian.rb0
-rw-r--r--lib/puppet/provider/service/freebsd.rb6
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/init.rb9
-rw-r--r--lib/puppet/provider/service/launchd.rb59
-rw-r--r--lib/puppet/provider/service/openbsd.rb23
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/redhat.rb0
-rw-r--r--lib/puppet/provider/service/runit.rb6
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/smf.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/src.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/systemd.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/service/upstart.rb6
-rw-r--r--lib/puppet/provider/ssh_authorized_key/parsed.rb4
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/sshkey/parsed.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/provider/user/aix.rb0
-rw-r--r--lib/puppet/provider/user/directoryservice.rb2
-rw-r--r--lib/puppet/provider/user/useradd.rb2
-rw-r--r--lib/puppet/provider/zone/solaris.rb2
-rw-r--r--lib/puppet/rails/benchmark.rb2
-rw-r--r--lib/puppet/reference/configuration.rb3
-rw-r--r--lib/puppet/reference/indirection.rb26
-rw-r--r--[-rwxr-xr-x]lib/puppet/relationship.rb11
-rw-r--r--[-rwxr-xr-x]lib/puppet/reports.rb4
-rw-r--r--lib/puppet/reports/rrdgraph.rb2
-rw-r--r--lib/puppet/reports/store.rb6
-rw-r--r--lib/puppet/reports/tagmail.rb4
-rw-r--r--lib/puppet/resource.rb74
-rw-r--r--lib/puppet/resource/catalog.rb43
-rw-r--r--lib/puppet/resource/status.rb14
-rw-r--r--lib/puppet/run.rb8
-rw-r--r--lib/puppet/settings.rb177
-rw-r--r--lib/puppet/settings/autosign_setting.rb20
-rw-r--r--lib/puppet/settings/base_setting.rb17
-rw-r--r--lib/puppet/settings/directory_setting.rb8
-rw-r--r--lib/puppet/settings/file_setting.rb36
-rw-r--r--lib/puppet/settings/priority_setting.rb42
-rw-r--r--lib/puppet/ssl.rb4
-rw-r--r--lib/puppet/ssl/certificate.rb18
-rw-r--r--lib/puppet/ssl/certificate_authority.rb173
-rw-r--r--lib/puppet/ssl/certificate_authority/autosign_command.rb44
-rw-r--r--lib/puppet/ssl/certificate_authority/interface.rb38
-rw-r--r--lib/puppet/ssl/certificate_factory.rb50
-rw-r--r--lib/puppet/ssl/certificate_request.rb248
-rw-r--r--lib/puppet/ssl/certificate_request_attributes.rb37
-rw-r--r--lib/puppet/ssl/certificate_revocation_list.rb4
-rw-r--r--lib/puppet/ssl/host.rb31
-rw-r--r--lib/puppet/ssl/inventory.rb16
-rw-r--r--lib/puppet/ssl/key.rb2
-rw-r--r--lib/puppet/ssl/oids.rb78
-rw-r--r--lib/puppet/ssl/validator.rb138
-rw-r--r--lib/puppet/ssl/validator/default_validator.rb153
-rw-r--r--lib/puppet/ssl/validator/no_validator.rb17
-rw-r--r--lib/puppet/status.rb4
-rw-r--r--lib/puppet/test/test_helper.rb5
-rw-r--r--lib/puppet/transaction.rb13
-rw-r--r--lib/puppet/transaction/event.rb11
-rw-r--r--lib/puppet/transaction/report.rb8
-rw-r--r--lib/puppet/transaction/resource_harness.rb288
-rw-r--r--lib/puppet/type.rb43
-rw-r--r--lib/puppet/type/augeas.rb58
-rw-r--r--lib/puppet/type/component.rb8
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/cron.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/exec.rb14
-rw-r--r--lib/puppet/type/file.rb29
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/checksum.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/content.rb3
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/ensure.rb48
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/group.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/mode.rb8
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/owner.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/source.rb79
-rw-r--r--lib/puppet/type/file/target.rb12
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/file/type.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/filebucket.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/group.rb18
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/host.rb0
-rw-r--r--lib/puppet/type/k5login.rb8
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/mailalias.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/maillist.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/mount.rb16
-rw-r--r--lib/puppet/type/package.rb8
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/port.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/schedule.rb13
-rw-r--r--lib/puppet/type/service.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/sshkey.rb0
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/tidy.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/user.rb8
-rw-r--r--lib/puppet/type/yumrepo.rb14
-rw-r--r--[-rwxr-xr-x]lib/puppet/type/zpool.rb0
-rw-r--r--lib/puppet/util.rb35
-rw-r--r--lib/puppet/util/adsi.rb94
-rw-r--r--lib/puppet/util/autoload.rb6
-rw-r--r--lib/puppet/util/backups.rb8
-rw-r--r--lib/puppet/util/cacher.rb20
-rw-r--r--lib/puppet/util/checksums.rb4
-rw-r--r--lib/puppet/util/classgen.rb4
-rw-r--r--lib/puppet/util/colors.rb1
-rw-r--r--lib/puppet/util/command_line.rb5
-rw-r--r--lib/puppet/util/docs.rb60
-rw-r--r--lib/puppet/util/execution.rb64
-rw-r--r--[-rwxr-xr-x]lib/puppet/util/filetype.rb6
-rw-r--r--[-rwxr-xr-x]lib/puppet/util/instance_loader.rb4
-rw-r--r--lib/puppet/util/instrumentation.rb65
-rw-r--r--lib/puppet/util/instrumentation/data.rb15
-rw-r--r--lib/puppet/util/instrumentation/indirection_probe.rb15
-rw-r--r--lib/puppet/util/instrumentation/instrumentable.rb21
-rw-r--r--lib/puppet/util/instrumentation/listener.rb23
-rw-r--r--lib/puppet/util/instrumentation/listeners/log.rb14
-rw-r--r--lib/puppet/util/instrumentation/listeners/performance.rb22
-rw-r--r--lib/puppet/util/limits.rb12
-rw-r--r--lib/puppet/util/lockfile.rb4
-rw-r--r--lib/puppet/util/log.rb20
-rw-r--r--lib/puppet/util/log/destinations.rb24
-rw-r--r--lib/puppet/util/metric.rb12
-rw-r--r--lib/puppet/util/monkey_patches.rb9
-rw-r--r--lib/puppet/util/network_device/config.rb2
-rw-r--r--lib/puppet/util/plugins.rb2
-rw-r--r--[-rwxr-xr-x]lib/puppet/util/posix.rb0
-rw-r--r--lib/puppet/util/profiler.rb9
-rw-r--r--lib/puppet/util/provider_features.rb4
-rw-r--r--lib/puppet/util/rdoc.rb58
-rw-r--r--lib/puppet/util/rdoc/code_objects.rb100
-rw-r--r--lib/puppet/util/rdoc/generators/puppet_generator.rb2
-rw-r--r--lib/puppet/util/rdoc/parser.rb499
-rw-r--r--lib/puppet/util/rdoc/parser/puppet_parser_core.rb477
-rw-r--r--lib/puppet/util/rdoc/parser/puppet_parser_rdoc1.rb19
-rw-r--r--lib/puppet/util/rdoc/parser/puppet_parser_rdoc2.rb14
-rw-r--r--lib/puppet/util/reference.rb2
-rw-r--r--lib/puppet/util/resource_template.rb2
-rw-r--r--lib/puppet/util/selinux.rb2
-rw-r--r--lib/puppet/util/storage.rb4
-rw-r--r--lib/puppet/util/suidmanager.rb2
-rw-r--r--lib/puppet/util/tag_set.rb29
-rw-r--r--lib/puppet/util/tagging.rb32
-rw-r--r--[-rwxr-xr-x]lib/puppet/util/watched_file.rb2
-rw-r--r--lib/puppet/util/watcher.rb2
-rw-r--r--lib/puppet/util/windows.rb3
-rw-r--r--lib/puppet/util/windows/access_control_entry.rb84
-rw-r--r--lib/puppet/util/windows/access_control_list.rb106
-rw-r--r--lib/puppet/util/windows/file.rb213
-rw-r--r--lib/puppet/util/windows/process.rb199
-rw-r--r--lib/puppet/util/windows/root_certs.rb89
-rw-r--r--lib/puppet/util/windows/security.rb516
-rw-r--r--lib/puppet/util/windows/security_descriptor.rb62
-rw-r--r--lib/puppet/util/windows/sid.rb30
-rw-r--r--lib/puppet/util/yaml.rb5
-rw-r--r--lib/puppet/version.rb4
-rw-r--r--spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb2
-rw-r--r--spec/fixtures/unit/indirector/data_binding/hiera/global.yaml (renamed from spec/fixtures/unit/indirector/hiera/global.yaml)0
-rw-r--r--spec/fixtures/unit/indirector/data_binding/hiera/invalid.yaml1
-rw-r--r--spec/fixtures/unit/module/trailing-comma.json24
-rw-r--r--spec/fixtures/unit/util/monkey_patches/x509.pem32
-rwxr-xr-xspec/integration/application/apply_spec.rb2
-rwxr-xr-xspec/integration/application/doc_spec.rb2
-rwxr-xr-xspec/integration/configurer_spec.rb6
-rw-r--r--spec/integration/data_binding.rb100
-rwxr-xr-xspec/integration/indirector/catalog/compiler_spec.rb29
-rwxr-xr-xspec/integration/indirector/direct_file_server_spec.rb8
-rwxr-xr-xspec/integration/indirector/file_content/file_server_spec.rb4
-rwxr-xr-xspec/integration/network/server/webrick_spec.rb76
-rwxr-xr-xspec/integration/node/facts_spec.rb2
-rwxr-xr-xspec/integration/node_spec.rb2
-rwxr-xr-xspec/integration/parser/compiler_spec.rb90
-rwxr-xr-xspec/integration/parser/functions_spec.rb16
-rwxr-xr-xspec/integration/parser/parser_spec.rb4
-rw-r--r--spec/integration/provider/cron/crontab_spec.rb8
-rwxr-xr-xspec/integration/resource/catalog_spec.rb2
-rw-r--r--spec/integration/ssl/autosign_spec.rb130
-rwxr-xr-xspec/integration/ssl/certificate_authority_spec.rb131
-rwxr-xr-xspec/integration/ssl/certificate_revocation_list_spec.rb2
-rwxr-xr-xspec/integration/ssl/host_spec.rb2
-rwxr-xr-xspec/integration/transaction_spec.rb26
-rwxr-xr-xspec/integration/type/exec_spec.rb4
-rwxr-xr-xspec/integration/type/file_spec.rb332
-rwxr-xr-xspec/integration/type/tidy_spec.rb6
-rwxr-xr-xspec/integration/util/rdoc/parser_spec.rb271
-rwxr-xr-xspec/integration/util/settings_spec.rb2
-rw-r--r--spec/integration/util/windows/process_spec.rb22
-rwxr-xr-xspec/integration/util/windows/security_spec.rb422
-rw-r--r--spec/lib/matchers/containment_matchers.rb52
-rw-r--r--spec/lib/puppet_spec/compiler.rb6
-rwxr-xr-xspec/lib/puppet_spec/files.rb41
-rw-r--r--spec/shared_behaviours/documentation_on_faces.rb6
-rwxr-xr-xspec/shared_behaviours/file_server_terminus.rb4
-rw-r--r--spec/shared_contexts/platform.rb1
-rwxr-xr-xspec/spec_helper.rb14
-rwxr-xr-xspec/unit/agent_spec.rb12
-rwxr-xr-xspec/unit/application/agent_spec.rb8
-rwxr-xr-xspec/unit/application/apply_spec.rb20
-rwxr-xr-xspec/unit/application/cert_spec.rb14
-rwxr-xr-xspec/unit/application/device_spec.rb2
-rwxr-xr-xspec/unit/application/filebucket_spec.rb2
-rwxr-xr-xspec/unit/application/inspect_spec.rb2
-rwxr-xr-xspec/unit/application_spec.rb24
-rwxr-xr-xspec/unit/configurer/downloader_spec.rb15
-rwxr-xr-xspec/unit/configurer/fact_handler_spec.rb23
-rwxr-xr-xspec/unit/configurer/plugin_handler_spec.rb9
-rwxr-xr-xspec/unit/configurer_spec.rb20
-rwxr-xr-xspec/unit/confine/exists_spec.rb (renamed from spec/unit/provider/confine/exists_spec.rb)24
-rwxr-xr-xspec/unit/confine/false_spec.rb (renamed from spec/unit/provider/confine/false_spec.rb)18
-rwxr-xr-xspec/unit/confine/feature_spec.rb (renamed from spec/unit/provider/confine/feature_spec.rb)20
-rwxr-xr-xspec/unit/confine/true_spec.rb (renamed from spec/unit/provider/confine/true_spec.rb)14
-rwxr-xr-xspec/unit/confine/variable_spec.rb (renamed from spec/unit/provider/confine/variable_spec.rb)32
-rwxr-xr-xspec/unit/confine_collection_spec.rb (renamed from spec/unit/provider/confine_collection_spec.rb)60
-rwxr-xr-xspec/unit/confine_spec.rb (renamed from spec/unit/provider/confine_spec.rb)22
-rwxr-xr-xspec/unit/confiner_spec.rb (renamed from spec/unit/provider/confiner_spec.rb)8
-rw-r--r--spec/unit/face/parser_spec.rb54
-rwxr-xr-xspec/unit/file_bucket/dipper_spec.rb4
-rwxr-xr-xspec/unit/file_serving/base_spec.rb41
-rwxr-xr-xspec/unit/file_serving/configuration_spec.rb14
-rwxr-xr-xspec/unit/file_serving/content_spec.rb19
-rwxr-xr-xspec/unit/file_serving/fileset_spec.rb84
-rwxr-xr-xspec/unit/file_serving/metadata_spec.rb86
-rwxr-xr-xspec/unit/file_serving/mount/file_spec.rb20
-rwxr-xr-xspec/unit/file_serving/mount/pluginfacts_spec.rb73
-rw-r--r--spec/unit/file_system/file_spec.rb486
-rw-r--r--spec/unit/file_system/tempfile_spec.rb48
-rwxr-xr-xspec/unit/graph/relationship_graph_spec.rb6
-rw-r--r--spec/unit/hiera_puppet_spec.rb4
-rwxr-xr-xspec/unit/indirector/catalog/compiler_spec.rb34
-rwxr-xr-xspec/unit/indirector/certificate_status/file_spec.rb70
-rw-r--r--spec/unit/indirector/data_binding/hiera_spec.rb97
-rwxr-xr-xspec/unit/indirector/direct_file_server_spec.rb12
-rwxr-xr-xspec/unit/indirector/facts/facter_spec.rb33
-rwxr-xr-xspec/unit/indirector/file_bucket_file/file_spec.rb113
-rwxr-xr-xspec/unit/indirector/file_metadata/file_spec.rb4
-rwxr-xr-xspec/unit/indirector/file_server_spec.rb8
-rw-r--r--spec/unit/indirector/hiera_spec.rb154
-rwxr-xr-xspec/unit/indirector/json_spec.rb8
-rwxr-xr-xspec/unit/indirector/key/file_spec.rb27
-rwxr-xr-xspec/unit/indirector/resource/ral_spec.rb7
-rwxr-xr-xspec/unit/indirector/resource/store_configs_spec.rb11
-rwxr-xr-xspec/unit/indirector/rest_spec.rb10
-rwxr-xr-xspec/unit/indirector/ssl_file_spec.rb31
-rwxr-xr-xspec/unit/indirector/yaml_spec.rb8
-rwxr-xr-xspec/unit/module_spec.rb58
-rw-r--r--spec/unit/module_tool/tar/gnu_spec.rb4
-rw-r--r--spec/unit/module_tool/tar/solaris_spec.rb4
-rw-r--r--spec/unit/module_tool/tar_spec.rb45
-rwxr-xr-xspec/unit/network/authconfig_spec.rb3
-rwxr-xr-xspec/unit/network/authentication_spec.rb4
-rwxr-xr-xspec/unit/network/format_handler_spec.rb4
-rwxr-xr-xspec/unit/network/formats_spec.rb24
-rw-r--r--spec/unit/network/http/connection_spec.rb275
-rwxr-xr-xspec/unit/network/http/handler_spec.rb67
-rwxr-xr-xspec/unit/network/http_pool_spec.rb13
-rwxr-xr-xspec/unit/node/environment_spec.rb166
-rwxr-xr-xspec/unit/node/facts_spec.rb23
-rwxr-xr-xspec/unit/node_spec.rb43
-rw-r--r--spec/unit/parameter/boolean_spec.rb34
-rw-r--r--spec/unit/parser/ast/resourceparam_spec.rb51
-rwxr-xr-xspec/unit/parser/compiler_spec.rb138
-rw-r--r--spec/unit/parser/eparser_adapter_spec.rb24
-rwxr-xr-xspec/unit/parser/files_spec.rb22
-rw-r--r--spec/unit/parser/functions/contain_spec.rb185
-rwxr-xr-xspec/unit/parser/functions/create_resources_spec.rb18
-rwxr-xr-xspec/unit/parser/functions/generate_spec.rb2
-rwxr-xr-xspec/unit/parser/functions_spec.rb4
-rwxr-xr-xspec/unit/parser/lexer_spec.rb2
-rw-r--r--spec/unit/parser/methods/collect_spec.rb153
-rw-r--r--spec/unit/parser/methods/each_spec.rb2
-rw-r--r--spec/unit/parser/methods/filter_spec.rb (renamed from spec/unit/parser/methods/select_spec.rb)22
-rwxr-xr-xspec/unit/parser/methods/foreach_spec.rb91
-rw-r--r--spec/unit/parser/methods/map_spec.rb95
-rw-r--r--spec/unit/parser/methods/reduce_spec.rb23
-rw-r--r--spec/unit/parser/methods/reject_spec.rb73
-rw-r--r--spec/unit/parser/methods/shared.rb10
-rw-r--r--spec/unit/parser/methods/slice_spec.rb26
-rwxr-xr-xspec/unit/parser/parser_spec.rb2
-rwxr-xr-xspec/unit/parser/resource/param_spec.rb44
-rwxr-xr-xspec/unit/parser/resource_spec.rb31
-rw-r--r--spec/unit/pops/model/ast_transformer_spec.rb22
-rwxr-xr-xspec/unit/pops/parser/lexer_spec.rb27
-rw-r--r--spec/unit/pops/parser/parse_calls_spec.rb10
-rw-r--r--spec/unit/pops/transformer/transform_calls_spec.rb12
-rw-r--r--spec/unit/pops/transformer/transform_containers_spec.rb4
-rw-r--r--spec/unit/pops/validator/validator_spec.rb31
-rwxr-xr-xspec/unit/provider/augeas/augeas_spec.rb59
-rwxr-xr-xspec/unit/provider/exec/posix_spec.rb11
-rwxr-xr-xspec/unit/provider/file/posix_spec.rb4
-rw-r--r--spec/unit/provider/group/windows_adsi_spec.rb75
-rwxr-xr-xspec/unit/provider/nameservice/directoryservice_spec.rb6
-rwxr-xr-xspec/unit/provider/package/apt_spec.rb2
-rwxr-xr-xspec/unit/provider/package/aptrpm_spec.rb2
-rwxr-xr-xspec/unit/provider/package/msi_spec.rb57
-rwxr-xr-xspec/unit/provider/package/openbsd_spec.rb6
-rwxr-xr-xspec/unit/provider/package/rpm_spec.rb79
-rwxr-xr-xspec/unit/provider/package/windows_spec.rb34
-rwxr-xr-xspec/unit/provider/package/yum_spec.rb2
-rwxr-xr-xspec/unit/provider/service/base_spec.rb2
-rwxr-xr-xspec/unit/provider/service/daemontools_spec.rb26
-rwxr-xr-xspec/unit/provider/service/freebsd_spec.rb6
-rwxr-xr-xspec/unit/provider/service/gentoo_spec.rb7
-rwxr-xr-xspec/unit/provider/service/init_spec.rb34
-rwxr-xr-xspec/unit/provider/service/launchd_spec.rb99
-rw-r--r--spec/unit/provider/service/openbsd_spec.rb125
-rwxr-xr-xspec/unit/provider/service/openwrt_spec.rb2
-rwxr-xr-xspec/unit/provider/service/runit_spec.rb17
-rwxr-xr-xspec/unit/provider/service/upstart_spec.rb8
-rwxr-xr-xspec/unit/provider/ssh_authorized_key/parsed_spec.rb10
-rwxr-xr-xspec/unit/provider/user/directoryservice_spec.rb8
-rwxr-xr-xspec/unit/provider/user/windows_adsi_spec.rb2
-rwxr-xr-xspec/unit/provider/zone/solaris_spec.rb2
-rwxr-xr-xspec/unit/provider_spec.rb4
-rwxr-xr-xspec/unit/reports/http_spec.rb53
-rwxr-xr-xspec/unit/reports/store_spec.rb4
-rwxr-xr-xspec/unit/resource/catalog_spec.rb92
-rw-r--r--spec/unit/resource/resource_type.json34
-rwxr-xr-xspec/unit/resource/status_spec.rb12
-rwxr-xr-xspec/unit/resource/type_spec.rb31
-rwxr-xr-xspec/unit/resource_spec.rb44
-rw-r--r--spec/unit/settings/autosign_setting_spec.rb46
-rwxr-xr-xspec/unit/settings/file_setting_spec.rb4
-rwxr-xr-xspec/unit/settings/path_setting_spec.rb4
-rwxr-xr-xspec/unit/settings/priority_setting_spec.rb66
-rwxr-xr-xspec/unit/settings_spec.rb74
-rw-r--r--spec/unit/ssl/certificate_authority/autosign_command_spec.rb30
-rwxr-xr-xspec/unit/ssl/certificate_authority_spec.rb263
-rwxr-xr-xspec/unit/ssl/certificate_factory_spec.rb18
-rw-r--r--spec/unit/ssl/certificate_request_attributes_spec.rb61
-rwxr-xr-xspec/unit/ssl/certificate_request_spec.rb103
-rwxr-xr-xspec/unit/ssl/certificate_spec.rb49
-rwxr-xr-xspec/unit/ssl/host_spec.rb42
-rwxr-xr-xspec/unit/ssl/inventory_spec.rb89
-rwxr-xr-xspec/unit/ssl/key_spec.rb8
-rw-r--r--spec/unit/ssl/oids_spec.rb48
-rw-r--r--spec/unit/ssl/validator_spec.rb55
-rwxr-xr-xspec/unit/status_spec.rb9
-rwxr-xr-xspec/unit/transaction/event_spec.rb10
-rwxr-xr-xspec/unit/transaction/report_spec.rb21
-rwxr-xr-xspec/unit/transaction/resource_harness_spec.rb270
-rwxr-xr-xspec/unit/transaction_spec.rb62
-rwxr-xr-xspec/unit/type/component_spec.rb4
-rwxr-xr-xspec/unit/type/exec_spec.rb21
-rwxr-xr-xspec/unit/type/file/content_spec.rb15
-rwxr-xr-xspec/unit/type/file/ctime_spec.rb2
-rwxr-xr-xspec/unit/type/file/mode_spec.rb50
-rwxr-xr-xspec/unit/type/file/mtime_spec.rb2
-rwxr-xr-xspec/unit/type/file/source_spec.rb184
-rwxr-xr-xspec/unit/type/file_spec.rb134
-rwxr-xr-xspec/unit/type/group_spec.rb20
-rwxr-xr-xspec/unit/type/k5login_spec.rb6
-rwxr-xr-xspec/unit/type/mount_spec.rb53
-rwxr-xr-xspec/unit/type/nagios_spec.rb216
-rwxr-xr-xspec/unit/type/package_spec.rb8
-rwxr-xr-xspec/unit/type/schedule_spec.rb6
-rwxr-xr-xspec/unit/type/service_spec.rb6
-rwxr-xr-xspec/unit/type/tidy_spec.rb28
-rwxr-xr-xspec/unit/type/user_spec.rb9
-rwxr-xr-xspec/unit/type_spec.rb90
-rwxr-xr-xspec/unit/util/adsi_spec.rb136
-rwxr-xr-xspec/unit/util/autoload_spec.rb28
-rwxr-xr-xspec/unit/util/backups_spec.rb50
-rwxr-xr-xspec/unit/util/checksums_spec.rb3
-rwxr-xr-xspec/unit/util/command_line_spec.rb41
-rw-r--r--spec/unit/util/docs_spec.rb91
-rwxr-xr-xspec/unit/util/execution_spec.rb32
-rwxr-xr-xspec/unit/util/filetype_spec.rb14
-rw-r--r--spec/unit/util/lockfile_spec.rb4
-rwxr-xr-xspec/unit/util/log/destinations_spec.rb32
-rwxr-xr-xspec/unit/util/monkey_patches_spec.rb41
-rw-r--r--spec/unit/util/pidlock_spec.rb12
-rwxr-xr-xspec/unit/util/rdoc/parser_spec.rb28
-rwxr-xr-xspec/unit/util/rdoc_spec.rb42
-rwxr-xr-xspec/unit/util/resource_template_spec.rb6
-rwxr-xr-xspec/unit/util/selinux_spec.rb6
-rwxr-xr-xspec/unit/util/storage_spec.rb8
-rwxr-xr-xspec/unit/util/suidmanager_spec.rb7
-rw-r--r--spec/unit/util/tag_set_spec.rb46
-rwxr-xr-xspec/unit/util/tagging_spec.rb127
-rw-r--r--spec/unit/util/watcher_spec.rb5
-rw-r--r--spec/unit/util/windows/access_control_entry_spec.rb67
-rw-r--r--spec/unit/util/windows/access_control_list_spec.rb133
-rwxr-xr-xspec/unit/util/windows/root_certs_spec.rb18
-rw-r--r--spec/unit/util/windows/security_descriptor_spec.rb117
-rwxr-xr-xspec/unit/util/windows/sid_spec.rb69
-rw-r--r--spec/unit/util/yaml_spec.rb14
-rwxr-xr-xspec/unit/util_spec.rb14
-rw-r--r--tasks/ci.rake29
598 files changed, 14453 insertions, 7915 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 96665c12d..681db4beb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -47,6 +47,28 @@ top of things.
* Make sure you have added the necessary tests for your changes.
* Run _all_ the tests to assure nothing else was accidentally broken.
+## Making Trivial Changes
+
+### Documentation
+
+For changes of a trivial nature to comments and documentation, it is not
+always necessary to create a new ticket in Redmine. In this case, it is
+appropriate to start the first line of a commit with '(doc)' instead of
+a ticket number.
+
+````
+ (doc) Add documentation commit example to CONTRIBUTING
+
+ There is no example for contributing a documentation commit
+ to the Puppet repository. This is a problem because the contributor
+ is left to assume how a commit of this nature may appear.
+
+ The first line is a real life imperative statement with '(doc)' in
+ place of what would have been the ticket number in a
+ non-documentation related commit. The body describes the nature of
+ the new documentation or comments added.
+````
+
## Submitting Changes
* Sign the [Contributor License Agreement](http://links.puppetlabs.com/cla).
diff --git a/Gemfile b/Gemfile
index bbd885072..5607f1fc5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,7 +1,7 @@
-source "https://rubygems.org"
+source ENV['GEM_SOURCE'] || "https://rubygems.org"
def location_for(place, fake_version = nil)
- if place =~ /^(git:[^#]*)#(.*)/
+ if place =~ /^(git[:@][^#]*)#(.*)/
[fake_version, { :git => $1, :branch => $2, :require => false }].compact
elsif place =~ /^file:\/\/(.*)/
['>= 0', { :path => File.expand_path($1), :require => false }]
@@ -15,7 +15,7 @@ platforms :ruby do
gem 'pry', :group => :development
gem 'yard', :group => :development
gem 'redcarpet', '~> 2.0', :group => :development
- gem "racc", "~> 1.4", :group => :development
+ gem "racc", "1.4.9", :group => :development
end
gem "puppet", :path => File.dirname(__FILE__), :require => false
@@ -37,6 +37,14 @@ group(:development, :test) do
gem "mocha", "~> 0.10.5", :require => false
gem "yarjuf", "~> 1.0"
+
+ # json-schema does not support windows, so use the 'ruby' platform to exclude it on windows
+ platforms :ruby do
+ # json-schema uses multi_json, but chokes with multi_json 1.7.9, so prefer 1.7.7
+ gem "multi_json", "1.7.7", :require => false
+ gem "json-schema", "2.1.1", :require => false
+ end
+
end
group(:extra) do
@@ -48,21 +56,23 @@ group(:extra) do
gem "sqlite3", :require => false
gem "stomp", :require => false
gem "tzinfo", :require => false
+ gem "msgpack", :require => false
end
platforms :mswin, :mingw do
- gem "sys-admin", "~> 1.5.6", :require => false
- gem "win32-api", "~> 1.4.8", :require => false
- gem "win32-dir", "~> 0.3.7", :require => false
- gem "win32-eventlog", "~> 0.5.3", :require => false
- gem "win32-process", "~> 0.6.5", :require => false
- gem "win32-security", "~> 0.1.4", :require => false
- gem "win32-service", "~> 0.7.2", :require => false
- gem "win32-taskscheduler", "~> 0.2.2", :require => false
- gem "win32console", "~> 1.3.2", :require => false
- gem "windows-api", "~> 0.4.2", :require => false
- gem "windows-pr", "~> 1.2.1", :require => false
- gem "minitar", "~> 0.5.4", :require => false
+ gem "ffi", "1.9.0", :require => false
+ gem "sys-admin", "1.5.6", :require => false
+ gem "win32-api", "1.4.8", :require => false
+ gem "win32-dir", "0.4.3", :require => false
+ gem "win32-eventlog", "0.5.3", :require => false
+ gem "win32-process", "0.6.5", :require => false
+ gem "win32-security", "0.1.4", :require => false
+ gem "win32-service", "0.7.2", :require => false
+ gem "win32-taskscheduler", "0.2.2", :require => false
+ gem "win32console", "1.3.2", :require => false
+ gem "windows-api", "0.4.2", :require => false
+ gem "windows-pr", "1.2.2", :require => false
+ gem "minitar", "0.5.4", :require => false
end
if File.exists? "#{__FILE__}.local"
diff --git a/README.md b/README.md
index 33de06ad1..648e92327 100644
--- a/README.md
+++ b/README.md
@@ -7,32 +7,28 @@ Puppet, an automated administrative engine for your Linux, Unix, and Windows sys
administrative tasks (such as adding users, installing packages, and updating server
configurations) based on a centralized specification.
-Documentation (and detailed installation instructions) can be found online at the
-[Puppet Docs site](http://docs.puppetlabs.com).
+Documentation
+-------------
+Documentation for Puppet and related projects can be found online at the
+[Puppet Docs site](http://docs.puppetlabs.com).
Installation
------------
-Generally, you need the following things installed:
-
-* A supported Ruby version. Ruby 1.8.7, and 1.9.3 are fully supported.
+The best way to run Puppet is with [Puppet Enterprise](http://puppetlabs.com/puppet/puppet-enterprise),
+which also includes orchestration features, a web console, and professional support.
+[The PE documentation is available here.](http://docs.puppetlabs.com/pe/latest)
-* The Ruby OpenSSL library. For some reason, this often isn't included
- in the main ruby distributions. You can test for it by running
- `ruby -ropenssl -e "puts :yep"`. If that errors out, you're missing the
- library.
+To install an open source release of Puppet,
+[see the installation guide on the docs site.](http://docs.puppetlabs.com/guides/installation.html)
- If your distribution doesn't come with the necessary library (e.g., on Debian
- and Ubuntu you need to install libopenssl-ruby), then you'll probably have to
- compile Ruby yourself, since it's part of the standard library and not
- available separately. You could probably just compile and install that one
- library, though.
-
-* Facter => 1.6.11 (available via your package manager or from the [Facter site](http://puppetlabs.com/projects/facter)).
+If you need to run Puppet from source as a tester or developer,
+[see the running from source guide on the docs site.](http://docs.puppetlabs.com/guides/from_source.html)
Contributions
------
+
Please see our [Contribution
Documents](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md)
and our [Developer
@@ -54,4 +50,4 @@ is an active #puppet channel on Freenode.
HTTP API
--------
-{file:api_docs/http_api_index.md HTTP API Index}
+{file:api/docs/http_api_index.md HTTP API Index}
diff --git a/README_DEVELOPER.md b/README_DEVELOPER.md
index 7d1990899..aac8b0c25 100644
--- a/README_DEVELOPER.md
+++ b/README_DEVELOPER.md
@@ -693,7 +693,7 @@ When Puppet master is started from Rack, Puppet 3.x will read from
~/.puppet/puppet.conf by default. This is intended behavior. Rack
configurations should start Puppet master with an explicit configuration
directory using `ARGV << "--confdir" << "/etc/puppet"`. Please see the
-`ext/rack/files/config.ru` file for an up-to-date example.
+`ext/rack/config.ru` file for an up-to-date example.
# Determining the Puppet Version
diff --git a/Rakefile b/Rakefile
index 6002a029d..97172a50d 100644
--- a/Rakefile
+++ b/Rakefile
@@ -64,16 +64,5 @@ task :default do
end
task :spec do
- sh %{rspec -fd spec}
-end
-
-namespace "ci" do
- task :spec do
- ENV["LOG_SPEC_ORDER"] = "true"
- sh %{rspec -r yarjuf -f JUnit -o result.xml -fd spec}
- end
-
- task :el6tests do
- sh "cd acceptance/config/el6; rm -f el6.tar.gz; tar -czvf el6.tar.gz *"
- end
+ sh %{rspec spec}
end
diff --git a/examples/hiera/README.md b/examples/hiera/README.md
index 4fe73eb25..ecbbc944c 100644
--- a/examples/hiera/README.md
+++ b/examples/hiera/README.md
@@ -24,7 +24,7 @@ Module from forge with module defaults
<pre>
$ mv modules/data modules/data.bak
-$ puppet --config etc/puppet.conf --libdir ../lib site.pp
+$ puppet apply --config etc/puppet.conf site.pp
notice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/ensure: defined content as '{md5}7045121976147a932a66c7671939a9ad'
notice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'
$ cat /tmp/ntp.conf
@@ -40,7 +40,7 @@ Site wide override data in _data::common_
<pre>
$ mv modules/data.bak modules/data
-$ puppet --config etc/puppet.conf --libdir ../lib site.pp
+$ puppet apply --config etc/puppet.conf site.pp
notice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/content: content changed '{md5}7045121976147a932a66c7671939a9addc2' to '{md5}8f9039fe1989a278a0a8e1836acb8d23'
notice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'
$ cat /tmp/ntp.conf
@@ -56,7 +56,7 @@ Fact driven overrides for location=dc1
* The _hiera_include()_ function includes _users::common_ and _users::dc1_ as the data file for dc1 adds that
<pre>
-$ FACTER_location=dc1 puppet --config etc/puppet.conf --libdir ../lib site.pp
+$ FACTER_location=dc1 puppet apply --config etc/puppet.conf site.pp
notice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/content: content changed '{md5}8f9039fe1989a278a0a8e1836acb8d23' to '{md5}074d0e2ac727f6cb9afe3345d574b578'
notice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'
notice: /Stage[main]/Users::Dc1/Notify[Adding users::dc1]/message: defined 'message' as 'Adding users::dc1'
@@ -69,7 +69,7 @@ Now simulate a machine in _dc2_, because there is no data for dc2 it uses the si
does not include the _users::dc1_ class anymore
<pre>
-$ FACTER_location=dc2 puppet --config etc/puppet.conf --libdir ../lib site.pp
+$ FACTER_location=dc2 puppet apply --config etc/puppet.conf site.pp
warning: Could not find class data::dc2 for nephilim.ml.org
notice: /Stage[main]/Ntp::Config/File[/tmp/ntp.conf]/content: content changed '{md5}074d0e2ac727f6cb9afe3345d574b578' to '{md5}8f9039fe1989a278a0a8e1836acb8d23'
notice: /Stage[main]/Users::Common/Notify[Adding users::common]/message: defined 'message' as 'Adding users::common'
diff --git a/examples/mac_automount.pp b/examples/mac_automount.pp
deleted file mode 100644
index bab0136fc..000000000
--- a/examples/mac_automount.pp
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env puppet
-# Jeff McCune <mccune@math.ohio-state.edu>
-#
-# Apple's Automounter spawns a child that sends the parent
-# a SIGTERM. This makes it *very* difficult to figure out
-# if the process started correctly or not.
-#
-
-service {"automount-test":
- provider => base,
- hasrestart => false,
- pattern => '/tmp/hometest',
- start => "/usr/sbin/automount -m /tmp/home /dev/null -mnt /tmp/hometest",
- stop => "ps auxww | grep '/tmp/hometest' | grep -v grep | awk '{print \$2}' | xargs kill",
- ensure => running
-}
diff --git a/examples/mcx_dock_absent.pp b/examples/mcx_dock_absent.pp
deleted file mode 100644
index ef51897e0..000000000
--- a/examples/mcx_dock_absent.pp
+++ /dev/null
@@ -1,4 +0,0 @@
-mcx { '/Groups/mcx_dock':
- ensure => 'absent',
- content => 'absent'
-}
diff --git a/examples/mcx_dock_default.pp b/examples/mcx_dock_default.pp
deleted file mode 100644
index 78d678956..000000000
--- a/examples/mcx_dock_default.pp
+++ /dev/null
@@ -1,118 +0,0 @@
-mcx { '/Groups/mcx_dock':
- ensure => 'present',
- content => '<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.dock</key>
- <dict>
- <key>AppItems-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>AppItems-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>static-apps</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array>
- <dict>
- <key>mcx_typehint</key>
- <integer>1</integer>
- <key>tile-data</key>
- <dict>
- <key>file-data</key>
- <dict>
- <key>_CFURLString</key>
- <string>/Applications/Mail.app</string>
- <key>_CFURLStringType</key>
- <integer>0</integer>
- </dict>
- <key>file-label</key>
- <string>Mail</string>
- </dict>
- <key>tile-type</key>
- <string>file-tile</string>
- </dict>
- <dict>
- <key>mcx_typehint</key>
- <integer>1</integer>
- <key>tile-data</key>
- <dict>
- <key>file-data</key>
- <dict>
- <key>_CFURLString</key>
- <string>/Applications/Safari.app</string>
- <key>_CFURLStringType</key>
- <integer>0</integer>
- </dict>
- <key>file-label</key>
- <string>Safari</string>
- </dict>
- <key>tile-type</key>
- <string>file-tile</string>
- </dict>
- </array>
- </dict>
- <key>DocItems-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>DocItems-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>static-others</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array/>
- </dict>
- <key>MCXDockSpecialFolders-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>MCXDockSpecialFolders-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>MCXDockSpecialFolders</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array/>
- </dict>
- <key>contents-immutable</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>value</key>
- <false/>
- </dict>
- <key>static-only</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>value</key>
- <false/>
- </dict>
- </dict>
-</dict>
-</plist>
-'
-}
diff --git a/examples/mcx_dock_full.pp b/examples/mcx_dock_full.pp
deleted file mode 100644
index 4fbf508fa..000000000
--- a/examples/mcx_dock_full.pp
+++ /dev/null
@@ -1,125 +0,0 @@
-# Mac MCX Test
-
-computer { "localhost": }
-
-mcx {
- "mcx_dock":
- ensure => "present",
- ds_type => "group",
- ds_name => "mcx_dock",
- content => '<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.dock</key>
- <dict>
- <key>AppItems-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>AppItems-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>static-apps</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array>
- <dict>
- <key>mcx_typehint</key>
- <integer>1</integer>
- <key>tile-data</key>
- <dict>
- <key>file-data</key>
- <dict>
- <key>_CFURLString</key>
- <string>/Applications/Mail.app</string>
- <key>_CFURLStringType</key>
- <integer>0</integer>
- </dict>
- <key>file-label</key>
- <string>Mail</string>
- </dict>
- <key>tile-type</key>
- <string>file-tile</string>
- </dict>
- <dict>
- <key>mcx_typehint</key>
- <integer>1</integer>
- <key>tile-data</key>
- <dict>
- <key>file-data</key>
- <dict>
- <key>_CFURLString</key>
- <string>/Applications/Safari.app</string>
- <key>_CFURLStringType</key>
- <integer>0</integer>
- </dict>
- <key>file-label</key>
- <string>Safari</string>
- </dict>
- <key>tile-type</key>
- <string>file-tile</string>
- </dict>
- </array>
- </dict>
- <key>DocItems-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>DocItems-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>static-others</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array/>
- </dict>
- <key>MCXDockSpecialFolders-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>MCXDockSpecialFolders-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>MCXDockSpecialFolders</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array/>
- </dict>
- <key>contents-immutable</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>value</key>
- <false/>
- </dict>
- <key>static-only</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>value</key>
- <false/>
- </dict>
- </dict>
-</dict>
-</plist>
-'
-}
diff --git a/examples/mcx_dock_invalid.pp b/examples/mcx_dock_invalid.pp
deleted file mode 100644
index 35f908177..000000000
--- a/examples/mcx_dock_invalid.pp
+++ /dev/null
@@ -1,9 +0,0 @@
-# Mac MCX Test
-
-computer { "localhost": }
-
-mcx {
- "/Groups/mcx_dock":
- ensure => "present",
- content => 'invalid plist'
-}
diff --git a/examples/mcx_nogroup.pp b/examples/mcx_nogroup.pp
deleted file mode 100644
index 67d20e605..000000000
--- a/examples/mcx_nogroup.pp
+++ /dev/null
@@ -1,118 +0,0 @@
-mcx { '/Groups/doesnotexist':
- ensure => 'present',
- content => '<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.dock</key>
- <dict>
- <key>AppItems-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>AppItems-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>static-apps</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array>
- <dict>
- <key>mcx_typehint</key>
- <integer>1</integer>
- <key>tile-data</key>
- <dict>
- <key>file-data</key>
- <dict>
- <key>_CFURLString</key>
- <string>/Applications/Mail.app</string>
- <key>_CFURLStringType</key>
- <integer>0</integer>
- </dict>
- <key>file-label</key>
- <string>Mail</string>
- </dict>
- <key>tile-type</key>
- <string>file-tile</string>
- </dict>
- <dict>
- <key>mcx_typehint</key>
- <integer>1</integer>
- <key>tile-data</key>
- <dict>
- <key>file-data</key>
- <dict>
- <key>_CFURLString</key>
- <string>/Applications/Safari.app</string>
- <key>_CFURLStringType</key>
- <integer>0</integer>
- </dict>
- <key>file-label</key>
- <string>Safari</string>
- </dict>
- <key>tile-type</key>
- <string>file-tile</string>
- </dict>
- </array>
- </dict>
- <key>DocItems-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>DocItems-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>static-others</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array/>
- </dict>
- <key>MCXDockSpecialFolders-Raw</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>upk</key>
- <dict>
- <key>mcx_input_key_names</key>
- <array>
- <string>MCXDockSpecialFolders-Raw</string>
- </array>
- <key>mcx_output_key_name</key>
- <string>MCXDockSpecialFolders</string>
- <key>mcx_remove_duplicates</key>
- <true/>
- </dict>
- <key>value</key>
- <array/>
- </dict>
- <key>contents-immutable</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>value</key>
- <false/>
- </dict>
- <key>static-only</key>
- <dict>
- <key>state</key>
- <string>always</string>
- <key>value</key>
- <false/>
- </dict>
- </dict>
-</dict>
-</plist>
-'
-}
diff --git a/examples/mcx_notexists_absent.pp b/examples/mcx_notexists_absent.pp
deleted file mode 100644
index ef44db10e..000000000
--- a/examples/mcx_notexists_absent.pp
+++ /dev/null
@@ -1,4 +0,0 @@
-mcx { '/Groups/foobar':
- ensure => 'absent',
- content => 'absent'
-}
diff --git a/ext/build_defaults.yaml b/ext/build_defaults.yaml
index 71636e6a0..1d117b3d4 100644
--- a/ext/build_defaults.yaml
+++ b/ext/build_defaults.yaml
@@ -2,14 +2,14 @@
packaging_url: 'git://github.com/puppetlabs/packaging.git --branch=master'
packaging_repo: 'packaging'
default_cow: 'base-squeeze-i386.cow'
-cows: 'base-lucid-i386.cow base-precise-i386.cow base-quantal-i386.cow base-raring-i386.cow base-sid-i386.cow base-squeeze-i386.cow base-stable-i386.cow base-testing-i386.cow base-unstable-i386.cow base-wheezy-i386.cow'
+cows: 'base-lucid-i386.cow base-precise-i386.cow base-quantal-i386.cow base-raring-i386.cow base-sid-i386.cow base-squeeze-i386.cow base-stable-i386.cow base-testing-i386.cow base-unstable-i386.cow base-wheezy-i386.cow base-saucy-i386.cow'
pbuild_conf: '/etc/pbuilderrc'
packager: 'puppetlabs'
gpg_name: 'info@puppetlabs.com'
gpg_key: '4BD6EC30'
sign_tar: FALSE
# a space separated list of mock configs
-final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-fedora-18-i386 pl-fedora-19-i386'
+final_mocks: 'pl-el-5-i386 pl-el-6-i386 pl-fedora-18-i386 pl-fedora-19-i386 pl-fedora-20-i386'
yum_host: 'yum.puppetlabs.com'
yum_repo_path: '/opt/repository/yum/'
build_gem: TRUE
diff --git a/ext/debian/changelog b/ext/debian/changelog
index b94c8e5b7..eb381b0fd 100644
--- a/ext/debian/changelog
+++ b/ext/debian/changelog
@@ -1,8 +1,8 @@
-puppet (3.3.1-1puppetlabs1) hardy lucid natty oneiric unstable sid squeeze wheezy precise; urgency=low
+puppet (3.4.0-1puppetlabs1) hardy lucid natty oneiric unstable sid squeeze wheezy precise; urgency=low
- * Update to version 3.3.1-1puppetlabs1
+ * Update to version 3.4.0-1puppetlabs1
- -- Puppet Labs Release <info@puppetlabs.com> Mon, 07 Oct 2013 11:00:57 -0700
+ -- Puppet Labs Release <info@puppetlabs.com> Thu, 19 Dec 2013 10:53:11 -0800
puppet (3.2.3-0.1rc0puppetlabs1) lucid unstable sid squeeze wheezy precise quantal raring; urgency=low
diff --git a/ext/debian/puppetmaster.init b/ext/debian/puppetmaster.init
index 4ebcc5a73..d3dffac33 100644
--- a/ext/debian/puppetmaster.init
+++ b/ext/debian/puppetmaster.init
@@ -100,6 +100,7 @@ status_puppet_master() {
else
echo ""
echo "puppetmaster not configured to start"
+ status_of_proc -p "/var/run/puppet/${NAME}.pid" "${DAEMON}" "${NAME}"
fi
}
diff --git a/ext/debian/rules b/ext/debian/rules
index 92ac1c5bb..11418e57f 100755
--- a/ext/debian/rules
+++ b/ext/debian/rules
@@ -66,14 +66,11 @@ install: build
$(INSTALL) -m0644 ext/emacs/puppet-mode.el \
$(CURDIR)/debian/puppet-el/usr/share/emacs/site-lisp/puppet-mode.el
- # Install the rack README as README.rack
- $(INSTALL) -m0644 ext/rack/README \
- $(CURDIR)/debian/puppetmaster-passenger/usr/share/doc/puppetmaster-passenger/README.rack
# Install the config.ru
- $(INSTALL) -m0644 ext/rack/files/config.ru \
+ $(INSTALL) -m0644 ext/rack/config.ru \
$(CURDIR)/debian/puppetmaster-passenger/usr/share/puppet/rack/puppetmasterd
# Install apache2 site configuration template
- $(INSTALL) -m0644 ext/rack/files/apache2.conf \
+ $(INSTALL) -m0644 ext/rack/example-passenger-vhost.conf \
$(CURDIR)/debian/puppetmaster-passenger/usr/share/puppetmaster-passenger/apache2.site.conf.tmpl
# Add ext directory
diff --git a/ext/ips/puppet.p5m b/ext/ips/puppet.p5m
index 06cb710ff..958a952b9 100644
--- a/ext/ips/puppet.p5m
+++ b/ext/ips/puppet.p5m
@@ -1,6 +1,6 @@
-set name=pkg.fmri value=pkg://puppetlabs.com/system/management/puppet@3.3.1,12.5.0-0
+set name=pkg.fmri value=pkg://puppetlabs.com/system/management/puppet@3.4.0,13.0.0-0
set name=pkg.summary value="Puppet, an automated configuration management tool"
-set name=pkg.human-version value="3.3.1"
+set name=pkg.human-version value="3.4.0"
set name=pkg.description value="Puppet, an automated configuration management tool"
set name=info.classification value="org.opensolaris.category.2008:System/Administration and Configuration"
set name=org.opensolaris.consolidation value="puppet"
diff --git a/ext/nagios/check_puppet.rb b/ext/nagios/check_puppet.rb
index 85807f122..e614b3c77 100755
--- a/ext/nagios/check_puppet.rb
+++ b/ext/nagios/check_puppet.rb
@@ -99,16 +99,16 @@ class CheckPuppet
process = "process #{OPTIONS[:process]} is not running"
end
- case @proc or @file
- when 0
- status = "OK"
- exitcode = 0
- when 2
+ case
+ when (@proc == 2 or @file == 2)
status = "CRITICAL"
exitcode = 2
- when 3
+ when (@proc == 0 and @file == 0)
+ status = "OK"
+ exitcode = 0
+ else
status = "UNKNOWN"
- exitcide = 3
+ exitcode = 3
end
puts "PUPPET #{status}: #{process}, #{state}"
diff --git a/ext/osx/file_mapping.yaml b/ext/osx/file_mapping.yaml
index 9a1e957d7..7a1d4e7f7 100644
--- a/ext/osx/file_mapping.yaml
+++ b/ext/osx/file_mapping.yaml
@@ -1,6 +1,6 @@
directories:
lib:
- path: 'usr/lib/ruby/site_ruby/1.8'
+ path: 'Library/Ruby/Site'
owner: 'root'
group: 'wheel'
perms: '0644'
diff --git a/ext/osx/preflight.erb b/ext/osx/preflight.erb
index d04f30560..036ac11d7 100755
--- a/ext/osx/preflight.erb
+++ b/ext/osx/preflight.erb
@@ -8,30 +8,45 @@
# ${3} is the destination volume so that this works correctly
# when being installed to volumes other than the current OS.
-<% begin %>
-<% require 'rubygems' %>
-<% rescue LoadError %>
-<% end %>
-<% require 'rake' %>
+<%- ["@apple_libdir", "@apple_sbindir", "@apple_bindir", "@apple_docdir", "@package_name"].each do |i| -%>
+ <%- val = instance_variable_get(i) -%>
+ <%- raise "Critical variable #{i} is unset!" if val.nil? or val.empty? -%>
+<%- end -%>
-<% Dir.chdir("lib") %>
-<% FileList["**/*"].select {|i| File.file?(i)}.each do |file| %>
-/bin/rm -Rf "${3}<%= @apple_libdir %>/<%=file%>"
-<% end %>
+# remove ruby library files. Because hiera and puppet both place a "hiera"
+# directory in the libdir, we have to be sure we only delete the files
+# belonging to this project. In this case, we only delete files from within
+# the 'hiera' directory, while deleting the entire puppet directory.
+<%- Dir.chdir("lib") do -%>
+ <%- [@apple_old_libdir, @apple_libdir].compact.each do |libdir| -%>
+ <%- Dir["hiera/**/*"].select{ |i| File.file?(i) }.each do |file| -%>
+/bin/rm -f "${3}<%= libdir %>/<%= file %>"
+ <%- end -%>
+ <%- Dir.glob("*").each do |file| -%>
+ <%- next if file == "hiera" -%>
+ <%- if File.directory?(file) -%>
+/bin/rm -Rf "${3}<%= libdir %>/<%= file %>"
+ <%- else -%>
+/bin/rm -f "${3}<%= libdir %>/<%= file %>"
+ <%- end -%>
+ <%- end -%>
+ <%- end -%>
+<%- end -%>
-<% Dir.chdir("../bin") %>
-<% FileList["**/*"].select {|i| File.file?(i)}.each do |file| %>
-/bin/rm -Rf "${3}<%= @apple_bindir %>/<%=file%>"
-<% end %>
-<% Dir.chdir("..") %>
+# remove bin files
+<%- Dir.chdir("bin") do -%>
+ <%- Dir.glob("*").each do |file| -%>
+/bin/rm -f "${3}<%= @apple_bindir %>/<%= file %>"
+ <%- end -%>
+<%- end -%>
# remove old doc files
/bin/rm -Rf "${3}<%= @apple_docdir %>/<%= @package_name %>"
# These files used to live in the sbindir but were
# removed in Pupppet >= 3.0. Remove them
-/bin/rm -Rf "${3}<%= @apple_sbindir %>/puppetca"
-/bin/rm -Rf "${3}<%= @apple_sbindir %>/puppetd"
-/bin/rm -Rf "${3}<%= @apple_sbindir %>/puppetmasterd"
-/bin/rm -Rf "${3}<%= @apple_sbindir %>/puppetqd"
-/bin/rm -Rf "${3}<%= @apple_sbindir %>/puppetrun"
+/bin/rm -f "${3}<%= @apple_sbindir %>/puppetca"
+/bin/rm -f "${3}<%= @apple_sbindir %>/puppetd"
+/bin/rm -f "${3}<%= @apple_sbindir %>/puppetmasterd"
+/bin/rm -f "${3}<%= @apple_sbindir %>/puppetqd"
+/bin/rm -f "${3}<%= @apple_sbindir %>/puppetrun"
diff --git a/ext/rack/README b/ext/rack/README
deleted file mode 100644
index 3a422f6a1..000000000
--- a/ext/rack/README
+++ /dev/null
@@ -1,58 +0,0 @@
-
-PUPPETMASTER AS A RACK APPLICATION
-==================================
-
-puppetmaster can now be hosted as a standard Rack application. A proper
-config.ru is provided for this.
-
-For more details about rack, see http://rack.rubyforge.org/ .
-
-Getting started
-===============
-
-You'll need rack installed, version 1.0.0. Older versions are known not
-to work.
-
-
-WEBrick
--------
-
-WEBrick is currently not supported as a Rack host. You'll be better off
-just running puppetmasterd directly.
-
-
-Apache with Passenger (aka mod_rails)
--------------------------------------
-
-Make sure puppetmasterd ran at least once, so the CA & SSL certificates
-got set up.
-
-Requirements:
- Passenger version 2.2.2 or 2.2.5 or newer***
- Rack version 1.0.0
- Apache 2.x
- SSL Module loaded
-
-Apache configuration snippet is in files/apache2.conf. You need to
-edit it to reflect your servername.
-
-Required puppet.conf settings:
- [puppetmasterd]
- ssl_client_header = SSL_CLIENT_S_DN
- ssl_client_verify_header = SSL_CLIENT_VERIFY
-
-To set up most of the boring stuff, you can use this command:
- puppet --verbose --modulepath ./ext ext/rack/manifest.pp
-Or use manifest.pp as a starting point for your own module.
-
-Note: Passenger will not let applications run as root or the Apache user,
-instead an implicit setuid will be done, to the user whom owns
-config.ru. Therefore, config.ru shall be owned by the puppet user.
-
-
-*** Important note about Passenger versions:
- 2.2.2 is known to work.
- 2.2.3-2.2.4 are known to *NOT* work.
- 2.2.5 works again when used with Puppet 0.25.2+.
- Passenger installation doc: http://www.modrails.com/install.html
-
diff --git a/ext/rack/files/config.ru b/ext/rack/config.ru
index 984017edd..984017edd 100644
--- a/ext/rack/files/config.ru
+++ b/ext/rack/config.ru
diff --git a/ext/rack/files/apache2.conf b/ext/rack/example-passenger-vhost.conf
index 59bbd15b8..860e6f373 100644
--- a/ext/rack/files/apache2.conf
+++ b/ext/rack/example-passenger-vhost.conf
@@ -1,3 +1,9 @@
+# This Apache 2 virtual host config shows how to use Puppet as a Rack
+# application via Passenger. See
+# http://docs.puppetlabs.com/guides/passenger.html for more information.
+
+# You can also use the included config.ru file to run Puppet with other Rack
+# servers instead of Passenger.
# you probably want to tune these settings
PassengerHighPerformance on
diff --git a/ext/rack/manifest.pp b/ext/rack/manifest.pp
deleted file mode 100644
index 7df07c0c8..000000000
--- a/ext/rack/manifest.pp
+++ /dev/null
@@ -1,59 +0,0 @@
-
-file { ["/etc/puppet/rack", "/etc/puppet/rack/public"]:
- ensure => directory,
- mode => 0755,
- owner => root,
- group => root,
-}
-file { "/etc/puppet/rack/config.ru":
- ensure => present,
- source => "puppet:///modules/rack/config.ru",
- mode => 0644,
- owner => puppet,
- group => root,
-}
-file { "/etc/apache2/conf.d/puppetmasterd":
- ensure => present,
- source => "puppet:///modules/rack/apache2.conf",
- mode => 0644,
- owner => root,
- group => root,
- require => [File["/etc/puppet/rack/config.ru"], File["/etc/puppet/rack/public"], Package["apache2"], Package["passenger"]],
- notify => Service["apache2"],
-}
-
-package { ["rack", "passenger"]:
- ensure => installed,
- provider => "gem",
-}
-
-service { "apache2":
-}
-
-case $lsbdistid {
- "Debian": {
- package { ["apache2-mpm-worker", "apache2-threaded-dev", "apache2"]:
- ensure => installed,
- }
- file { "/etc/apache2/mods-enabled/ssl.load":
- ensure => "../mods-available/ssl.load",
- notify => Service["apache2"],
- require => Package["apache2"],
- }
- Service["apache2"] {
- require => Package["apache2"],
- }
- exec { "/var/lib/gems/1.8/bin/passenger-install-apache2-module --auto":
- subscribe => Package["passenger"],
- before => Service["apache2"],
- require => Package[["passenger", "apache2-threaded-dev"]],
- }
- }
-}
-
-notice("You need to manually enable mod_passenger.so for Apache.")
-notice("Usually, you put these config stanzas into httpd.conf:")
-notice(" LoadModule passenger_module /var/lib/gems/1.8/gems/passenger-2.2.2/ext/apache2/mod_passenger.so")
-notice(" PassengerRoot /var/lib/gems/1.8/gems/passenger-2.2.2")
-notice(" PassengerRuby /usr/bin/ruby1.8")
-notice("--------------------------------------------------------")
diff --git a/ext/redhat/puppet.spec b/ext/redhat/puppet.spec
index b92662873..4a38607ae 100644
--- a/ext/redhat/puppet.spec
+++ b/ext/redhat/puppet.spec
@@ -16,8 +16,8 @@
%endif
# VERSION is subbed out during rake srpm process
-%global realversion 3.3.1
-%global rpmversion 3.3.1
+%global realversion 3.4.0
+%global rpmversion 3.4.0
%global confdir ext/redhat
@@ -122,7 +122,8 @@ install -d -m0750 %{buildroot}%{_localstatedir}/log/puppet
%if 0%{?_with_systemd}
# Systemd for fedora >= 17
%{__install} -d -m0755 %{buildroot}%{_unitdir}
-install -Dp -m0644 ext/systemd/puppetagent.service %{buildroot}%{_unitdir}/puppetagent.service
+install -Dp -m0644 ext/systemd/puppet.service %{buildroot}%{_unitdir}/puppet.service
+ln -s %{_unitdir}/puppet.service %{buildroot}%{_unitdir}/puppetagent.service
install -Dp -m0644 ext/systemd/puppetmaster.service %{buildroot}%{_unitdir}/puppetmaster.service
%else
# Otherwise init.d for fedora < 17 or el 5, 6
@@ -189,6 +190,7 @@ cp -pr ext/puppet-nm-dispatcher \
%dir %{_sysconfdir}/NetworkManager/dispatcher.d
%{_sysconfdir}/NetworkManager/dispatcher.d/98-puppet
%if 0%{?_with_systemd}
+%{_unitdir}/puppet.service
%{_unitdir}/puppetagent.service
%else
%{_initrddir}/puppet
@@ -289,7 +291,7 @@ if [ "$1" -ge 1 ]; then
newpid="%{_localstatedir}/run/puppet/agent.pid"
if [ -s "$oldpid" -a ! -s "$newpid" ]; then
(kill $(< "$oldpid") && rm -f "$oldpid" && \
- /bin/systemctl start puppetagent.service) >/dev/null 2>&1 || :
+ /bin/systemctl start puppet.service) >/dev/null 2>&1 || :
fi
fi
%else
@@ -303,6 +305,15 @@ if [ "$1" -ge 1 ]; then
(kill $(< "$oldpid") && rm -f "$oldpid" && \
/sbin/service puppet start) >/dev/null 2>&1 || :
fi
+
+ # If an old puppet process (one whose binary is located in /sbin) is running,
+ # kill it and then start up a fresh with the new binary.
+ if [ -e "$newpid" ]; then
+ if ps aux | grep `cat "$newpid"` | grep -v grep | awk '{ print $12 }' | grep -q sbin; then
+ (kill $(< "$newpid") && rm -f "$newpid" && \
+ /sbin/service puppet start) >/dev/null 2>&1 || :
+ fi
+ fi
fi
%endif
@@ -338,7 +349,9 @@ fi
if [ "$1" -eq 0 ] ; then
# Package removal, not upgrade
/bin/systemctl --no-reload disable puppetagent.service > /dev/null 2>&1 || :
+ /bin/systemctl --no-reload disable puppet.service > /dev/null 2>&1 || :
/bin/systemctl stop puppetagent.service > /dev/null 2>&1 || :
+ /bin/systemctl stop puppet.service > /dev/null 2>&1 || :
/bin/systemctl daemon-reload >/dev/null 2>&1 || :
fi
%else
@@ -391,8 +404,13 @@ fi
rm -rf %{buildroot}
%changelog
-* Mon Oct 07 2013 Puppet Labs Release <info@puppetlabs.com> - 3.3.1-1
-- Build for 3.3.1
+* Thu Dec 19 2013 Puppet Labs Release <info@puppetlabs.com> - 3.4.0-1
+- Build for 3.4.0
+
+* Wed Oct 2 2013 Jason Antman <jason@jasonantman.com>
+- Move systemd service and unit file names back to "puppet" from erroneous "puppetagent"
+- Add symlink to puppetagent unit file for compatibility with current bug
+- Alter package removal actions to deactivate and stop both service names
* Thu Jun 27 2013 Matthaus Owens <matthaus@puppetlabs.com> - 3.2.3-0.1rc0
- Bump requires on ruby-rgen to 0.6.5
diff --git a/ext/systemd/puppetagent.service b/ext/systemd/puppet.service
index 6312a0509..6312a0509 100644
--- a/ext/systemd/puppetagent.service
+++ b/ext/systemd/puppet.service
diff --git a/lib/hiera_puppet.rb b/lib/hiera_puppet.rb
index 0e6a572a8..d90b82089 100644
--- a/lib/hiera_puppet.rb
+++ b/lib/hiera_puppet.rb
@@ -71,12 +71,12 @@ module HieraPuppet
if Puppet.settings[:hiera_config].is_a?(String)
expanded_config_file = File.expand_path(Puppet.settings[:hiera_config])
- if File.exist?(expanded_config_file)
+ if Puppet::FileSystem::File.exist?(expanded_config_file)
config_file = expanded_config_file
end
elsif Puppet.settings[:confdir].is_a?(String)
expanded_config_file = File.expand_path(File.join(Puppet.settings[:confdir], '/hiera.yaml'))
- if File.exist?(expanded_config_file)
+ if Puppet::FileSystem::File.exist?(expanded_config_file)
config_file = expanded_config_file
end
end
diff --git a/lib/puppet/agent.rb b/lib/puppet/agent.rb
index 678fdb5d0..14c6c693b 100644
--- a/lib/puppet/agent.rb
+++ b/lib/puppet/agent.rb
@@ -1,4 +1,3 @@
-require 'sync'
require 'puppet/application'
# A general class for triggering a run of another
@@ -42,7 +41,7 @@ class Puppet::Agent
with_client do |client|
begin
client_args = client_options.merge(:pluginsync => Puppet[:pluginsync])
- sync.synchronize { lock { client.run(client_args) } }
+ lock { client.run(client_args) }
rescue SystemExit,NoMemoryError
raise
rescue Exception => detail
@@ -76,10 +75,6 @@ class Puppet::Agent
@splayed = true
end
- def sync
- @sync ||= Sync.new
- end
-
def run_in_fork(forking = true)
return yield unless forking or Puppet.features.windows?
diff --git a/lib/puppet/application.rb b/lib/puppet/application.rb
index 463c77b32..acef32134 100644
--- a/lib/puppet/application.rb
+++ b/lib/puppet/application.rb
@@ -381,18 +381,31 @@ class Application
Puppet::Util::Log.newdestination(:console)
end
+ set_log_level
+
+ Puppet::Util::Log.setup_default unless options[:setdest]
+ end
+
+ def set_log_level
if options[:debug]
Puppet::Util::Log.level = :debug
elsif options[:verbose]
Puppet::Util::Log.level = :info
end
+ end
- Puppet::Util::Log.setup_default unless options[:setdest]
+ def handle_logdest_arg(arg)
+ begin
+ Puppet::Util::Log.newdestination(arg)
+ options[:setdest] = true
+ rescue => detail
+ Puppet.log_exception(detail)
+ end
end
def configure_indirector_routes
route_file = Puppet[:route_file]
- if ::File.exists?(route_file)
+ if Puppet::FileSystem::File.exist?(route_file)
routes = YAML.load_file(route_file)
application_routes = routes[name.to_s]
Puppet::Indirector.configure_routes(application_routes) if application_routes
diff --git a/lib/puppet/application/agent.rb b/lib/puppet/application/agent.rb
index 3a42dd83e..d86f3f47f 100644
--- a/lib/puppet/application/agent.rb
+++ b/lib/puppet/application/agent.rb
@@ -67,12 +67,7 @@ class Puppet::Application::Agent < Puppet::Application
end
option("--logdest DEST", "-l DEST") do |arg|
- begin
- Puppet::Util::Log.newdestination(arg)
- options[:setdest] = true
- rescue => detail
- Puppet.log_exception(detail)
- end
+ handle_logdest_arg(arg)
end
option("--waitforcert WAITFORCERT", "-w") do |arg|
@@ -442,7 +437,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
def setup_listen(daemon)
Puppet.warning "Puppet --listen / kick is deprecated. See http://links.puppetlabs.com/puppet-kick-deprecation"
- unless FileTest.exists?(Puppet[:rest_authconfig])
+ unless Puppet::FileSystem::File.exist?(Puppet[:rest_authconfig])
Puppet.err "Will not start without authorization file #{Puppet[:rest_authconfig]}"
exit(14)
end
diff --git a/lib/puppet/application/apply.rb b/lib/puppet/application/apply.rb
index 3d7370d2c..2eb4415b3 100644
--- a/lib/puppet/application/apply.rb
+++ b/lib/puppet/application/apply.rb
@@ -19,12 +19,7 @@ class Puppet::Application::Apply < Puppet::Application
end
option("--logdest LOGDEST", "-l") do |arg|
- begin
- Puppet::Util::Log.newdestination(arg)
- options[:logset] = true
- rescue => detail
- $stderr.puts detail.to_s
- end
+ handle_logdest_arg(arg)
end
option("--parseonly") do |args|
@@ -168,7 +163,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
Puppet[:code] = options[:code] || STDIN.read
else
manifest = command_line.args.shift
- raise "Could not find file #{manifest}" unless ::File.exist?(manifest)
+ raise "Could not find file #{manifest}" unless Puppet::FileSystem::File.exist?(manifest)
Puppet.warning("Only one file can be applied per run. Skipping #{command_line.args.join(', ')}") if command_line.args.size > 0
Puppet[:manifest] = manifest
end
@@ -194,7 +189,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
# Allow users to load the classes that puppet agent creates.
if options[:loadclasses]
file = Puppet[:classfile]
- if FileTest.exists?(file)
+ if Puppet::FileSystem::File.exist?(file)
unless FileTest.readable?(file)
$stderr.puts "#{file} is not readable"
exit(63)
@@ -238,7 +233,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
def setup
exit(Puppet.settings.print_configs ? 0 : 1) if Puppet.settings.print_configs?
- Puppet::Util::Log.newdestination(:console) unless options[:logset]
+ Puppet::Util::Log.newdestination(:console) unless options[:setdest]
Signal.trap(:INT) do
$stderr.puts "Exiting"
@@ -248,10 +243,10 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
# we want the last report to be persisted locally
Puppet::Transaction::Report.indirection.cache_class = :yaml
- if options[:debug]
- Puppet::Util::Log.level = :debug
- elsif options[:verbose]
- Puppet::Util::Log.level = :info
+ set_log_level
+
+ if Puppet[:profile]
+ Puppet::Util::Profiler.current = Puppet::Util::Profiler::WallClock.new(Puppet.method(:debug), "apply")
end
end
diff --git a/lib/puppet/application/cert.rb b/lib/puppet/application/cert.rb
index edf15f3b9..6ad901593 100644
--- a/lib/puppet/application/cert.rb
+++ b/lib/puppet/application/cert.rb
@@ -1,4 +1,5 @@
require 'puppet/application'
+require 'puppet/ssl/certificate_authority/interface'
class Puppet::Application::Cert < Puppet::Application
@@ -36,11 +37,36 @@ class Puppet::Application::Cert < Puppet::Application
Puppet::Util::Log.level = :debug
end
- require 'puppet/ssl/certificate_authority/interface'
- Puppet::SSL::CertificateAuthority::Interface::INTERFACE_METHODS.reject {|m| m == :destroy }.each do |method|
- option("--#{method.to_s.gsub('_','-')}", "-#{method.to_s[0,1]}") do |arg|
- self.subcommand = method
- end
+ option("--list", "-l") do |arg|
+ self.subcommand = :list
+ end
+
+ option("--revoke", "-r") do |arg|
+ self.subcommand = :revoke
+ end
+
+ option("--generate", "-g") do |arg|
+ self.subcommand = :generate
+ end
+
+ option("--sign", "-s") do |arg|
+ self.subcommand = :sign
+ end
+
+ option("--print", "-p") do |arg|
+ self.subcommand = :print
+ end
+
+ option("--verify", "-v") do |arg|
+ self.subcommand = :verify
+ end
+
+ option("--fingerprint", "-f") do |arg|
+ self.subcommand = :fingerprint
+ end
+
+ option("--reinventory") do |arg|
+ self.subcommand = :reinventory
end
option("--[no-]allow-dns-alt-names") do |value|
@@ -120,6 +146,11 @@ unless the '--all' option is set.
* verify:
Verify the named certificate against the local CA certificate.
+* reinventory:
+ Build an inventory of the issued certificates. This will destroy the current
+ inventory file specified by 'cert_inventory' and recreate it from the
+ certificates found in the 'certdir'. Ensure the puppet master is stopped
+ before running this action.
OPTIONS
-------
@@ -184,8 +215,8 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
hosts = command_line.args.collect { |h| h.downcase }
end
begin
- @ca.apply(:revoke, options.merge(:to => hosts)) if subcommand == :destroy
- @ca.apply(subcommand, options.merge(:to => hosts, :digest => @digest))
+ apply(@ca, :revoke, options.merge(:to => hosts)) if subcommand == :destroy
+ apply(@ca, subcommand, options.merge(:to => hosts, :digest => @digest))
rescue => detail
Puppet.log_exception(detail)
exit(24)
@@ -234,4 +265,13 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
end
result
end
+
+ # Create and run an applicator. I wanted to build an interface where you could do
+ # something like 'ca.apply(:generate).to(:all) but I don't think it's really possible.
+ def apply(ca, method, options)
+ raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
+ applier = Puppet::SSL::CertificateAuthority::Interface.new(method, options)
+ applier.apply(ca)
+ end
+
end
diff --git a/lib/puppet/application/device.rb b/lib/puppet/application/device.rb
index b80691f3c..5eef49fef 100644
--- a/lib/puppet/application/device.rb
+++ b/lib/puppet/application/device.rb
@@ -47,12 +47,7 @@ class Puppet::Application::Device < Puppet::Application
end
option("--logdest DEST", "-l DEST") do |arg|
- begin
- Puppet::Util::Log.newdestination(arg)
- options[:setdest] = true
- rescue => detail
- Puppet.log_exception(detail)
- end
+ handle_logdest_arg(arg)
end
option("--waitforcert WAITFORCERT", "-w") do |arg|
diff --git a/lib/puppet/application/face_base.rb b/lib/puppet/application/face_base.rb
index 86cc6a0d8..3154d6de4 100644
--- a/lib/puppet/application/face_base.rb
+++ b/lib/puppet/application/face_base.rb
@@ -240,7 +240,7 @@ class Puppet::Application::FaceBase < Puppet::Application
rescue SystemExit => detail
status = detail.status
- rescue Exception => detail
+ rescue => detail
Puppet.log_exception(detail)
Puppet.err "Try 'puppet help #{@face.name} #{@action.name}' for usage"
diff --git a/lib/puppet/application/filebucket.rb b/lib/puppet/application/filebucket.rb
index 389e6e4ca..509885602 100644
--- a/lib/puppet/application/filebucket.rb
+++ b/lib/puppet/application/filebucket.rb
@@ -129,7 +129,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
raise "You must specify a file to back up" unless args.length > 0
args.each do |file|
- unless FileTest.exists?(file)
+ unless Puppet::FileSystem::File.exist?(file)
$stderr.puts "#{file}: no such file"
next
end
diff --git a/lib/puppet/application/inspect.rb b/lib/puppet/application/inspect.rb
index 3ebeb7875..3aec9fa23 100644
--- a/lib/puppet/application/inspect.rb
+++ b/lib/puppet/application/inspect.rb
@@ -8,12 +8,7 @@ class Puppet::Application::Inspect < Puppet::Application
option("--verbose","-v")
option("--logdest LOGDEST", "-l") do |arg|
- begin
- Puppet::Util::Log.newdestination(arg)
- options[:logset] = true
- rescue => detail
- $stderr.puts detail.to_s
- end
+ handle_logdest_arg(arg)
end
def help
@@ -86,18 +81,14 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
@report = Puppet::Transaction::Report.new("inspect")
Puppet::Util::Log.newdestination(@report)
- Puppet::Util::Log.newdestination(:console) unless options[:logset]
+ Puppet::Util::Log.newdestination(:console) unless options[:setdest]
Signal.trap(:INT) do
$stderr.puts "Exiting"
exit(1)
end
- if options[:debug]
- Puppet::Util::Log.level = :debug
- elsif options[:verbose]
- Puppet::Util::Log.level = :info
- end
+ set_log_level
Puppet::Transaction::Report.indirection.terminus_class = :rest
Puppet::Resource::Catalog.indirection.terminus_class = :yaml
diff --git a/lib/puppet/application/master.rb b/lib/puppet/application/master.rb
index fafbcf3b4..b8f27994f 100644
--- a/lib/puppet/application/master.rb
+++ b/lib/puppet/application/master.rb
@@ -17,12 +17,7 @@ class Puppet::Application::Master < Puppet::Application
end
option("--logdest DEST", "-l DEST") do |arg|
- begin
- Puppet::Util::Log.newdestination(arg)
- options[:setdest] = true
- rescue => detail
- Puppet.log_exception(detail)
- end
+ handle_logdest_arg(arg)
end
option("--parseonly") do |args|
diff --git a/lib/puppet/application/queue.rb b/lib/puppet/application/queue.rb
index 87e269e33..e3820e621 100644
--- a/lib/puppet/application/queue.rb
+++ b/lib/puppet/application/queue.rb
@@ -112,12 +112,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
end
option("--logdest DEST", "-l DEST") do |arg|
- begin
- Puppet::Util::Log.newdestination(arg)
- options[:setdest] = true
- rescue => detail
- Puppet.log_exception(detail)
- end
+ handle_logdest_arg(arg)
end
def main
diff --git a/lib/puppet/application/resource.rb b/lib/puppet/application/resource.rb
index d4944f860..35f9f155b 100644
--- a/lib/puppet/application/resource.rb
+++ b/lib/puppet/application/resource.rb
@@ -14,6 +14,7 @@ class Puppet::Application::Resource < Puppet::Application
option("--edit","-e")
option("--host HOST","-H") do |arg|
+ Puppet.warning("Accessing resources on the network is deprecated. See http://links.puppetlabs.com/deprecate-networked-resource")
@host = arg
end
@@ -151,12 +152,7 @@ Copyright (c) 2011 Puppet Labs, LLC Licensed under the Apache 2.0 License
def setup
Puppet::Util::Log.newdestination(:console)
-
- if options[:debug]
- Puppet::Util::Log.level = :debug
- elsif options[:verbose]
- Puppet::Util::Log.level = :info
- end
+ set_log_level
end
private
diff --git a/lib/puppet/coercion.rb b/lib/puppet/coercion.rb
index f3db8f62b..5de06a363 100644
--- a/lib/puppet/coercion.rb
+++ b/lib/puppet/coercion.rb
@@ -26,4 +26,15 @@ module Puppet::Coercion
fail('expected a boolean value')
end
end
+
+ # Return the list of acceptable boolean values.
+ #
+ # This is limited to lower-case, even though boolean() is case-insensitive.
+ #
+ # @return [Array]
+ # @raise
+ # @api private
+ def self.boolean_values
+ ['true', 'false', 'yes', 'no']
+ end
end
diff --git a/lib/puppet/configurer.rb b/lib/puppet/configurer.rb
index 6547f7458..0c000c9ea 100644
--- a/lib/puppet/configurer.rb
+++ b/lib/puppet/configurer.rb
@@ -44,7 +44,7 @@ class Puppet::Configurer
rescue => detail
Puppet.log_exception(detail, "Removing corrupt state file #{Puppet[:statefile]}: #{detail}")
begin
- ::File.unlink(Puppet[:statefile])
+ Puppet::FileSystem::File.unlink(Puppet[:statefile])
retry
rescue => detail
raise Puppet::Error.new("Cannot remove #{Puppet[:statefile]}: #{detail}")
@@ -157,7 +157,9 @@ class Puppet::Configurer
query_options = nil
end
end
- rescue Puppet::Error, Net::HTTPError => detail
+ rescue SystemExit,NoMemoryError
+ raise
+ rescue Exception => detail
Puppet.warning("Unable to fetch my node definition, but the agent run will continue:")
Puppet.warning(detail)
end
@@ -199,7 +201,7 @@ class Puppet::Configurer
# Between Puppet runs we need to forget the cached values. This lets us
# pick up on new functions installed by gems or new modules being added
# without the daemon being restarted.
- Thread.current[:env_module_directories] = nil
+ $env_module_directories = nil
Puppet::Util::Log.close(report)
send_report(report)
diff --git a/lib/puppet/configurer/downloader.rb b/lib/puppet/configurer/downloader.rb
index f9aa161ca..56de33608 100644
--- a/lib/puppet/configurer/downloader.rb
+++ b/lib/puppet/configurer/downloader.rb
@@ -58,7 +58,9 @@ class Puppet::Configurer::Downloader
:backup => false,
:noop => false
}.merge(
- Puppet.features.microsoft_windows? ? {} :
+ Puppet.features.microsoft_windows? ? {
+ :source_permissions => :ignore
+ } :
{
:owner => Process.uid,
:group => Process.gid
diff --git a/lib/puppet/configurer/plugin_handler.rb b/lib/puppet/configurer/plugin_handler.rb
index f0a93eef4..87a307b70 100644
--- a/lib/puppet/configurer/plugin_handler.rb
+++ b/lib/puppet/configurer/plugin_handler.rb
@@ -11,6 +11,16 @@ module Puppet::Configurer::PluginHandler
Puppet[:pluginsignore],
@environment
)
+ if Puppet.features.external_facts?
+ plugin_fact_downloader = Puppet::Configurer::Downloader.new(
+ "pluginfacts",
+ Puppet[:pluginfactdest],
+ Puppet[:pluginfactsource],
+ Puppet[:pluginsignore],
+ @environment
+ )
+ plugin_fact_downloader.evaluate
+ end
plugin_downloader.evaluate
Puppet::Util::Autoload.reload_changed
diff --git a/lib/puppet/confine.rb b/lib/puppet/confine.rb
new file mode 100644
index 000000000..41b5aa0e6
--- /dev/null
+++ b/lib/puppet/confine.rb
@@ -0,0 +1,80 @@
+# The class that handles testing whether our providers
+# actually work or not.
+require 'puppet/util'
+
+class Puppet::Confine
+ include Puppet::Util
+
+ @tests = {}
+
+ class << self
+ attr_accessor :name
+ end
+
+ def self.inherited(klass)
+ name = klass.to_s.split("::").pop.downcase.to_sym
+ raise "Test #{name} is already defined" if @tests.include?(name)
+
+ klass.name = name
+
+ @tests[name] = klass
+ end
+
+ def self.test(name)
+ unless @tests[name]
+ begin
+ require "puppet/confine/#{name}"
+ rescue LoadError => detail
+ unless detail.to_s =~ /No such file|cannot load such file/i
+ warn "Could not load confine test '#{name}': #{detail}"
+ end
+ # Could not find file
+ end
+ end
+ @tests[name]
+ end
+
+ attr_reader :values
+
+ # Mark that this confine is used for testing binary existence.
+ attr_accessor :for_binary
+ def for_binary?
+ for_binary
+ end
+
+ # Used for logging.
+ attr_accessor :label
+
+ def initialize(values)
+ values = [values] unless values.is_a?(Array)
+ @values = values
+ end
+
+ # Provide a hook for the message when there's a failure.
+ def message(value)
+ ""
+ end
+
+ # Collect the results of all of them.
+ def result
+ values.collect { |value| pass?(value) }
+ end
+
+ # Test whether our confine matches.
+ def valid?
+ values.each do |value|
+ unless pass?(value)
+ Puppet.debug(label + ": " + message(value))
+ return false
+ end
+ end
+
+ return true
+ ensure
+ reset
+ end
+
+ # Provide a hook for subclasses.
+ def reset
+ end
+end
diff --git a/lib/puppet/provider/confine/exists.rb b/lib/puppet/confine/exists.rb
index 09f94dfd9..95a315514 100644
--- a/lib/puppet/provider/confine/exists.rb
+++ b/lib/puppet/confine/exists.rb
@@ -1,12 +1,12 @@
-require 'puppet/provider/confine'
+require 'puppet/confine'
-class Puppet::Provider::Confine::Exists < Puppet::Provider::Confine
+class Puppet::Confine::Exists < Puppet::Confine
def self.summarize(confines)
confines.inject([]) { |total, confine| total + confine.summary }
end
def pass?(value)
- value && (for_binary? ? which(value) : FileTest.exist?(value))
+ value && (for_binary? ? which(value) : Puppet::FileSystem::File.exist?(value))
end
def message(value)
diff --git a/lib/puppet/provider/confine/false.rb b/lib/puppet/confine/false.rb
index 1c11dd40f..3b664df3b 100644
--- a/lib/puppet/provider/confine/false.rb
+++ b/lib/puppet/confine/false.rb
@@ -1,6 +1,6 @@
-require 'puppet/provider/confine'
+require 'puppet/confine'
-class Puppet::Provider::Confine::False < Puppet::Provider::Confine
+class Puppet::Confine::False < Puppet::Confine
def self.summarize(confines)
confines.inject(0) { |count, confine| count + confine.summary }
end
diff --git a/lib/puppet/provider/confine/feature.rb b/lib/puppet/confine/feature.rb
index b223b8b11..5aff48a90 100644
--- a/lib/puppet/provider/confine/feature.rb
+++ b/lib/puppet/confine/feature.rb
@@ -1,6 +1,6 @@
-require 'puppet/provider/confine'
+require 'puppet/confine'
-class Puppet::Provider::Confine::Feature < Puppet::Provider::Confine
+class Puppet::Confine::Feature < Puppet::Confine
def self.summarize(confines)
confines.collect { |c| c.values }.flatten.uniq.find_all { |value| ! confines[0].pass?(value) }
end
diff --git a/lib/puppet/provider/confine/true.rb b/lib/puppet/confine/true.rb
index 559f2675f..b7e347045 100644
--- a/lib/puppet/provider/confine/true.rb
+++ b/lib/puppet/confine/true.rb
@@ -1,6 +1,6 @@
-require 'puppet/provider/confine'
+require 'puppet/confine'
-class Puppet::Provider::Confine::True < Puppet::Provider::Confine
+class Puppet::Confine::True < Puppet::Confine
def self.summarize(confines)
confines.inject(0) { |count, confine| count + confine.summary }
end
diff --git a/lib/puppet/provider/confine/variable.rb b/lib/puppet/confine/variable.rb
index af8e5d314..9102d4577 100644
--- a/lib/puppet/provider/confine/variable.rb
+++ b/lib/puppet/confine/variable.rb
@@ -1,10 +1,10 @@
-require 'puppet/provider/confine'
+require 'puppet/confine'
# Require a specific value for a variable, either a Puppet setting
# or a Facter value. This class is a bit weird because the name
# is set explicitly by the ConfineCollection class -- from this class,
# it's not obvious how the name would ever get set.
-class Puppet::Provider::Confine::Variable < Puppet::Provider::Confine
+class Puppet::Confine::Variable < Puppet::Confine
# Provide a hash summary of failing confines -- the key of the hash
# is the name of the confine, and the value is the missing yet required values.
# Only returns failed values, not all required values.
diff --git a/lib/puppet/provider/confine_collection.rb b/lib/puppet/confine_collection.rb
index 46fd3baaf..7a2eba8b4 100644
--- a/lib/puppet/provider/confine_collection.rb
+++ b/lib/puppet/confine_collection.rb
@@ -1,8 +1,8 @@
# Manage a collection of confines, returning a boolean or
# helpful information.
-require 'puppet/provider/confine'
+require 'puppet/confine'
-class Puppet::Provider::ConfineCollection
+class Puppet::ConfineCollection
def confine(hash)
if hash.include?(:for_binary)
for_binary = true
@@ -11,11 +11,11 @@ class Puppet::Provider::ConfineCollection
for_binary = false
end
hash.each do |test, values|
- if klass = Puppet::Provider::Confine.test(test)
+ if klass = Puppet::Confine.test(test)
@confines << klass.new(values)
@confines[-1].for_binary = true if for_binary
else
- confine = Puppet::Provider::Confine.test(:variable).new(values)
+ confine = Puppet::Confine.test(:variable).new(values)
confine.name = test
@confines << confine
end
diff --git a/lib/puppet/provider/confiner.rb b/lib/puppet/confiner.rb
index a1a2fa593..57176e799 100644
--- a/lib/puppet/provider/confiner.rb
+++ b/lib/puppet/confiner.rb
@@ -1,10 +1,10 @@
-require 'puppet/provider/confine_collection'
+require 'puppet/confine_collection'
# The Confiner module contains methods for managing a Provider's confinement (suitability under given
# conditions). The intent is to include this module in an object where confinement management is wanted.
# It lazily adds an instance variable `@confine_collection` to the object where it is included.
#
-module Puppet::Provider::Confiner
+module Puppet::Confiner
# Confines a provider to be suitable only under the given conditions.
# The hash describes a confine using mapping from symbols to values or predicate code.
#
@@ -26,11 +26,11 @@ module Puppet::Provider::Confiner
confine_collection.confine(hash)
end
- # @return [Puppet::Provider::ConfineCollection] the collection of confines
+ # @return [Puppet::ConfineCollection] the collection of confines
# @api private
#
def confine_collection
- @confine_collection ||= Puppet::Provider::ConfineCollection.new(self.to_s)
+ @confine_collection ||= Puppet::ConfineCollection.new(self.to_s)
end
# Checks whether this implementation is suitable for the current platform (or returns a summary
diff --git a/lib/puppet/daemon.rb b/lib/puppet/daemon.rb
index 562602418..c931184f1 100755..100644
--- a/lib/puppet/daemon.rb
+++ b/lib/puppet/daemon.rb
@@ -149,16 +149,12 @@ class Puppet::Daemon
# Create a pidfile for our daemon, so we can be stopped and others
# don't try to start.
def create_pidfile
- Puppet::Util.synchronize_on(Puppet.run_mode.name,Sync::EX) do
- raise "Could not create PID file: #{@pidfile.file_path}" unless @pidfile.lock
- end
+ raise "Could not create PID file: #{@pidfile.file_path}" unless @pidfile.lock
end
# Remove the pid file for our daemon.
def remove_pidfile
- Puppet::Util.synchronize_on(Puppet.run_mode.name,Sync::EX) do
- @pidfile.unlock
- end
+ @pidfile.unlock
end
def run_event_loop
diff --git a/lib/puppet/data_binding.rb b/lib/puppet/data_binding.rb
index ce77a9bf8..76cc7a6f3 100644
--- a/lib/puppet/data_binding.rb
+++ b/lib/puppet/data_binding.rb
@@ -2,39 +2,11 @@ require 'puppet/indirector'
# A class for managing data lookups
class Puppet::DataBinding
+ class LookupError < Puppet::Error; end
- # Set up indirection, so that data can be looked for in the complier
+ # Set up indirection, so that data can be looked for in the compiler
extend Puppet::Indirector
indirects(:data_binding, :terminus_setting => :data_binding_terminus,
:doc => "Where to find external data bindings.")
-
- # A class that acts just enough like a Puppet::Parser::Scope to
- # fool Hiera's puppet backend. This class doesn't actually do anything
- # but it does allow people to use the puppet backend with the hiera
- # data bindings withough causing problems.
- class Variables
- FAKE_RESOURCE = Struct.new(:name).new("fake").freeze
- FAKE_CATALOG = Struct.new(:classes).new([].freeze).freeze
-
- def initialize(variable_bindings)
- @variable_bindings = variable_bindings
- end
-
- def [](name)
- @variable_bindings[name]
- end
-
- def resource
- FAKE_RESOURCE
- end
-
- def catalog
- FAKE_CATALOG
- end
-
- def function_include(name)
- # noop
- end
- end
end
diff --git a/lib/puppet/defaults.rb b/lib/puppet/defaults.rb
index 7a161fc57..9b9502f06 100644
--- a/lib/puppet/defaults.rb
+++ b/lib/puppet/defaults.rb
@@ -20,23 +20,27 @@ module Puppet
:confdir => {
:default => nil,
:type => :directory,
- :desc =>
- "The main Puppet configuration directory. The default for this setting is calculated based on the user. If the process\n" +
- "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,\n" +
- "it defaults to being in the user's home directory.",
+ :desc => "The main Puppet configuration directory. The default for this setting
+ 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 => {
:default => nil,
:type => :directory,
- :desc => "Where Puppet stores dynamic and growing data. The default for this setting is calculated specially, like `confdir`_.",
+ :owner => "service",
+ :group => "service",
+ :desc => "Where Puppet stores dynamic and growing data. The default for this
+ setting is calculated specially, like `confdir`_.",
},
### NOTE: this setting is usually being set to a symbol value. We don't officially have a
### setting type for that yet, but we might want to consider creating one.
:name => {
:default => nil,
- :desc => "The name of the application, if we are running as one. The\n" +
- "default is essentially $0 without the path or `.rb`.",
+ :desc => "The name of the application, if we are running as one. The
+ default is essentially $0 without the path or `.rb`.",
}
)
@@ -52,6 +56,15 @@ module Puppet
)
define_settings(:main,
+ :priority => {
+ :default => nil,
+ :type => :priority,
+ :desc => "The scheduling priority of the process. Valid values are 'high',
+ 'normal', 'low', or 'idle', which are mapped to platform-specific
+ values. The priority can also be specified as an integer value and
+ will be passed as is, e.g. -5. Puppet must be running as a privileged
+ user in order to increase scheduling priority.",
+ },
:trace => {
:default => false,
:type => :boolean,
@@ -70,9 +83,9 @@ module Puppet
},
:syslogfacility => {
:default => "daemon",
- :desc => "What syslog facility to use when logging to\n" +
- "syslog. Syslog has a fixed list of valid facilities, and you must\n" +
- "choose one of those; you cannot just make one up."
+ :desc => "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",
@@ -93,30 +106,33 @@ module Puppet
:genconfig => {
:default => false,
:type => :boolean,
- :desc => "Whether to just print a configuration to stdout and exit. Only makes\n" +
- "sense when used interactively. Takes into account arguments specified\n" +
- "on the CLI.",
+ :desc => "When true, causes Puppet applications to print an example config file
+ to stdout and exit. The example will include descriptions of each
+ setting, and the current (or default) value of each setting,
+ incorporating any settings overridden on the CLI (with the exception
+ of `genconfig` itself). This setting only makes sense when specified
+ on the command line as `--genconfig`.",
},
:genmanifest => {
:default => false,
:type => :boolean,
- :desc => "Whether to just print a manifest to stdout and exit. Only makes\n" +
- "sense when used interactively. Takes into account arguments specified\n" +
- "on the CLI.",
+ :desc => "Whether to just print a manifest to stdout and exit. Only makes
+ sense when specified on the command line as `--genmanifest`. Takes into account arguments specified
+ on the CLI.",
},
:configprint => {
:default => "",
- :desc => "Print the value of a specific configuration setting. If the name of a\n" +
- "setting is provided for this, then the value is printed and puppet\n" +
- "exits. Comma-separate multiple values. For a list of all values,\n" +
- "specify 'all'.",
+ :desc => "Print the value of a specific configuration setting. If the name of a
+ setting is provided for this, then the value is printed and puppet
+ exits. Comma-separate multiple values. For a list of all values,
+ specify 'all'.",
},
:color => {
:default => "ansi",
:type => :string,
- :desc => "Whether to use colors when logging to the console. Valid values are\n" +
- "`ansi` (equivalent to `true`), `html`, and `false`, which produces no color.\n" +
- "Defaults to false on Windows, as its console does not support ansi colors.",
+ :desc => "Whether to use colors when logging to the console. Valid values are
+ `ansi` (equivalent to `true`), `html`, and `false`, which produces no color.
+ Defaults to false on Windows, as its console does not support ansi colors.",
},
:mkusers => {
:default => false,
@@ -131,14 +147,15 @@ module Puppet
:onetime => {
:default => false,
:type => :boolean,
- :desc => "Run the configuration once, rather than as a long-running\n" +
- "daemon. This is useful for interactively running puppetd.",
+ :desc => "Perform one configuration run and exit, rather than spawning a long-running
+ daemon. This is useful for interactively running puppet agent, or
+ running puppet agent from cron.",
:short => 'o',
},
:path => {
:default => "none",
- :desc => "The shell search path. Defaults to whatever is inherited\n" +
- "from the parent process.",
+ :desc => "The shell search path. Defaults to whatever is inherited
+ from the parent process.",
:call_hook => :on_define_and_write,
:hook => proc do |value|
ENV["PATH"] = "" if ENV["PATH"].nil?
@@ -153,11 +170,11 @@ module Puppet
:libdir => {
:type => :directory,
:default => "$vardir/lib",
- :desc => "An extra search path for Puppet. This is only useful\n" +
- "for those files that Puppet will load on demand, and is only\n" +
- "guaranteed to work for those cases. In fact, the autoload\n" +
- "mechanism is responsible for making sure this directory\n" +
- "is in Ruby's search path\n",
+ :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\n",
:call_hook => :on_initialize_and_write,
:hook => proc do |value|
$LOAD_PATH.delete(@oldlibdir) if defined?(@oldlibdir) and $LOAD_PATH.include?(@oldlibdir)
@@ -168,41 +185,44 @@ module Puppet
:ignoreimport => {
:default => false,
:type => :boolean,
- :desc => "If true, allows the parser to continue without requiring\n" +
- "all files referenced with `import` statements to exist. This setting was primarily\n" +
- "designed for use with commit hooks for parse-checking.",
+ :desc => "If true, allows the parser to continue without requiring
+ all files referenced with `import` statements to exist. This setting was primarily
+ designed for use with commit hooks for parse-checking.",
},
:environment => {
:default => "production",
- :desc => "The environment Puppet is running in. For clients\n" +
- "(e.g., `puppet agent`) this determines the environment itself, which\n" +
- "is used to find modules and much more. For servers (i.e., `puppet master`)\n" +
- "this provides the default environment for nodes we know nothing about."
+ :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 => {
:default => default_diffargs,
- :desc => "Which arguments to pass to the diff command when printing differences between\n" +
- "files. The command to use can be chosen with the `diff` setting.",
+ :desc => "Which arguments to pass to the diff command when printing differences between
+ files. The command to use can be chosen with the `diff` setting.",
},
:diff => {
:default => (Puppet.features.microsoft_windows? ? "" : "diff"),
- :desc => "Which diff command to use when printing differences between files. This setting\n" +
- "has no default value on Windows, as standard `diff` is not available, but Puppet can use many\n" +
- "third-party diff tools.",
+ :desc => "Which diff command to use when printing differences between files. This setting
+ has no default value on Windows, as standard `diff` is not available, but Puppet can use many
+ third-party diff tools.",
},
:show_diff => {
:type => :boolean,
:default => false,
- :desc => "Whether to log and report a contextual diff when files are being replaced. This causes\n" +
- "partial file contents to pass through Puppet's normal logging and reporting system, so this setting\n" +
- "should be used with caution if you are sending Puppet's reports to an insecure destination.\n" +
- "This feature currently requires the `diff/lcs` Ruby library.",
+ :desc => "Whether to log and report a contextual diff when files are being replaced.
+ This causes partial file contents to pass through Puppet's normal
+ logging and reporting system, so this setting should be used with
+ caution if you are sending Puppet's reports to an insecure
+ destination. This feature currently requires the `diff/lcs` Ruby
+ library.",
},
:daemonize => {
:type => :boolean,
:default => (Puppet.features.microsoft_windows? ? false : true),
- :desc => "Whether to send the process into the background. This defaults to true on POSIX systems,
- and to false on Windows (where Puppet currently cannot daemonize).",
+ :desc => "Whether to send the process into the background. This defaults
+ to true on POSIX systems, and to false on Windows (where Puppet
+ currently cannot daemonize).",
:short => "D",
:hook => proc do |value|
if value and Puppet.features.microsoft_windows?
@@ -212,10 +232,11 @@ module Puppet
},
:maximum_uid => {
:default => 4294967290,
- :desc => "The maximum allowed UID. Some platforms use negative UIDs\n" +
- "but then ship with tools that do not know how to handle signed ints, so the UIDs show up as\n" +
- "huge numbers that can then not be fed back into the system. This is a hackish way to fail in a\n" +
- "slightly more useful way when that happens.",
+ :desc => "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 => {
:default => "$confdir/routes.yaml",
@@ -385,14 +406,21 @@ module Puppet
:freeze_main => {
:default => false,
:type => :boolean,
- :desc => "Freezes the 'main' class, disallowing any code to be added to it. This\n" +
- "essentially means that you can't have any code outside of a node, class, or definition other\n" +
- "than in the site manifest.",
+ :desc => "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.",
},
:stringify_facts => {
:default => true,
:type => :boolean,
- :desc => "Flatten fact values to strings using #to_s. Means you can't have arrays or hashes as fact values.",
+ :desc => "Flatten fact values to strings using #to_s. Means you can't have arrays or
+ hashes as fact values.",
+ },
+ :trusted_node_data => {
+ :default => false,
+ :type => :boolean,
+ :desc => "Stores trusted node data in a hash called $trusted.
+ When true also prevents $trusted from being overridden in any scope.",
}
)
Puppet.define_settings(:module_tool,
@@ -472,6 +500,36 @@ have a pool of multiple load balanced masters, or for the same master to
respond on two physically separate networks under different names.
EOT
},
+ :csr_attributes => {
+ :default => "$confdir/csr_attributes.yaml",
+ :type => :file,
+ :desc => <<EOT
+An optional file containing custom attributes to add to certificate signing
+requests (CSRs). You should ensure that this file does not exist on your CA
+puppet master; if it does, unwanted certificate extensions may leak into
+certificates created with the `puppet cert generate` command.
+
+If present, this file must be a YAML hash containing a `custom_attributes` key
+and/or an `extension_requests` key. The value of each key must be a hash, where
+each key is a valid OID and each value is an object that can be cast to a string.
+
+Custom attributes can be used by the CA when deciding whether to sign the
+certificate, but are then discarded. Attribute OIDs can be any OID value except
+the standard CSR attributes (i.e. attributes described in RFC 2985 section 5.4).
+This is useful for embedding a pre-shared key for autosigning policy executables
+(see the `autosign` setting), often by using the `1.2.840.113549.1.9.7`
+("challenge password") OID.
+
+Extension requests will be permanently embedded in the final certificate.
+Extension OIDs must be in the "ppRegCertExt" (`1.3.6.1.4.1.34380.1.1`) or
+"ppPrivCertExt" (`1.3.6.1.4.1.34380.1.2`) OID arcs. The ppRegCertExt arc is
+reserved for four of the most common pieces of data to embed: `pp_uuid` (`.1`),
+`pp_instance_id` (`.2`), `pp_image_name` (`.3`), and `pp_preshared_key` (`.4`)
+--- in the YAML file, these can be referred to by their short descriptive names
+instead of their full OID. The ppPrivCertExt arc is unregulated, and can be used
+for site-specific extensions.
+EOT
+ },
:certdir => {
:default => "$ssldir/certs",
:type => :directory,
@@ -558,19 +616,19 @@ EOT
:type => :file,
:mode => 0644,
:owner => "service",
- :desc => "Certificate authorities who issue server certificates. SSL servers will not be \n" <<
- "considered authentic unless they posses a certificate issued by an authority \n" <<
- "listed in this file. If this setting has no value then the Puppet master's CA \n" <<
- "certificate (localcacert) will be used."
+ :desc => "Certificate authorities who issue server certificates. SSL servers will not be
+ considered authentic unless they posses a certificate issued by an authority
+ listed in this file. If this setting has no value then the Puppet master's CA
+ certificate (localcacert) will be used."
},
:ssl_server_ca_auth => {
:type => :file,
:mode => 0644,
:owner => "service",
- :desc => "Certificate authorities who issue client certificates. SSL clients will not be \n" <<
- "considered authentic unless they posses a certificate issued by an authority \n" <<
- "listed in this file. If this setting has no value then the Puppet master's CA \n" <<
- "certificate (localcacert) will be used."
+ :desc => "Certificate authorities who issue client certificates. SSL clients will not be
+ considered authentic unless they posses a certificate issued by an authority
+ listed in this file. If this setting has no value then the Puppet master's CA
+ certificate (localcacert) will be used."
},
:hostcrl => {
:default => "$ssldir/crl.pem",
@@ -583,8 +641,9 @@ EOT
:certificate_revocation => {
:default => true,
:type => :boolean,
- :desc => "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.",
+ :desc => "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.",
},
:certificate_expire_warning => {
:default => "60d",
@@ -681,12 +740,32 @@ EOT
},
:autosign => {
:default => "$confdir/autosign.conf",
- :type => :file,
- :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."},
+ :type => :autosign,
+ :desc => "Whether (and how) to autosign certificate requests. This setting
+ is only relevant on a puppet master acting as a certificate authority (CA).
+
+ Valid values are true (autosigns all certificate requests; not recommended),
+ false (disables autosigning certificates), or the absolute path to a file.
+
+ The file specified in this setting may be either a **configuration file**
+ or a **custom policy executable.** Puppet will automatically determine
+ what it is: If the Puppet user (see the `user` setting) can execute the
+ file, it will be treated as a policy executable; otherwise, it will be
+ treated as a config file.
+
+ If a custom policy executable is configured, the CA puppet master will run it
+ every time it receives a CSR. The executable will be passed the subject CN of the
+ request _as a command line argument,_ and the contents of the CSR in PEM format
+ _on stdin._ It should exit with a status of 0 if the cert should be autosigned
+ and non-zero if the cert should not be autosigned.
+
+ If a certificate request is not autosigned, it will persist for review. An admin
+ user can use the `puppet cert sign` command to manually sign it, or can delete
+ the request.
+
+ For info on autosign configuration files, see
+ [the guide to Puppet's config files](http://docs.puppetlabs.com/guides/configuring.html).",
+ },
:allow_duplicate_certs => {
:default => false,
:type => :boolean,
@@ -733,10 +812,10 @@ EOT
:pidfile => {
:type => :file,
:default => "$rundir/${run_mode}.pid",
- :desc => "The file containing the PID of a running process. " <<
- "This file is intended to be used by service management " <<
- "frameworks and monitoring systems to determine if a " <<
- "puppet process is still in the process table.",
+ :desc => "The file containing the PID of a running process.
+ This file is intended to be used by service management frameworks
+ and monitoring systems to determine if a puppet process is still in
+ the process table.",
},
:bindaddress => {
:default => "0.0.0.0",
@@ -825,8 +904,9 @@ EOT
:modulepath => {
:default => "$confdir/modules#{File::PATH_SEPARATOR}/usr/share/puppet/modules",
:type => :path,
- :desc => "The search path for modules, as a list of directories separated by the system path separator character. " +
- "(The POSIX path separator is ':', and the Windows path separator is ';'.)",
+ :desc => "The search path for modules, as a list of directories separated by the system
+ path separator character. (The POSIX path separator is ':', and the
+ Windows path separator is ';'.)",
},
:ssl_client_header => {
:default => "HTTP_X_CLIENT_DN",
@@ -1025,6 +1105,12 @@ EOT
:desc => "Boolean; whether puppet agent should ignore schedules. This is useful
for initial puppet agent runs.",
},
+ :default_schedules => {
+ :default => true,
+ :type => :boolean,
+ :desc => "Boolean; whether to generate the default schedule resources. Setting this to
+ false is useful for keeping external report processors clean of skipped schedule resources.",
+ },
:puppetport => {
:default => 8139,
:desc => "Which port puppet agent listens on.",
@@ -1114,49 +1200,50 @@ EOT
:agent_catalog_run_lockfile => {
:default => "$statedir/agent_catalog_run.lock",
:type => :string, # (#2888) Ensure this file is not added to the settings catalog.
- :desc => "A lock file to indicate that a puppet agent catalog run is currently in progress. " +
- "The file contains the pid of the process that holds the lock on the catalog run.",
+ :desc => "A lock file to indicate that a puppet agent catalog run is currently in progress.
+ The file contains the pid of the process that holds the lock on the catalog run.",
},
:agent_disabled_lockfile => {
:default => "$statedir/agent_disabled.lock",
- :type => :file,
- :desc => "A lock file to indicate that puppet agent runs have been administratively disabled. File contains a JSON object with state information.",
+ :type => :file,
+ :desc => "A lock file to indicate that puppet agent runs have been administratively
+ disabled. File contains a JSON object with state information.",
},
:usecacheonfailure => {
:default => true,
:type => :boolean,
:desc => "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.",
+ 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 => {
:default => false,
:type => :boolean,
:desc => "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.",
+ on every run. Puppet can be run with this enabled by default and then selectively
+ disabled when a recompile is desired.",
},
:ignoremissingtypes => {
:default => false,
:type => :boolean,
:desc => "Skip searching for classes and definitions that were missing during a
- prior compilation. The list of missing objects is maintained per-environment and
- persists until the environment is cleared or the master is restarted.",
+ prior compilation. The list of missing objects is maintained per-environment and
+ persists until the environment is cleared or the master is restarted.",
},
:ignorecache => {
:default => false,
:type => :boolean,
:desc => "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.",
+ 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.",
},
:dynamicfacts => {
:default => "memorysize,memoryfree,swapsize,swapfree",
:desc => "(Deprecated) 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.",
+ changed facts should result in a recompile. Multiple facts should be
+ comma-separated.",
:hook => proc { |value|
if value
Puppet.deprecation_warning "The dynamicfacts setting is deprecated and will be ignored."
@@ -1167,13 +1254,13 @@ EOT
:default => "$runinterval",
:type => :duration,
:desc => "The maximum time to delay before runs. Defaults to being the same as the
- run interval. #{AS_DURATION}",
+ run interval. #{AS_DURATION}",
},
:splay => {
:default => false,
:type => :boolean,
:desc => "Whether to sleep for a pseudo-random (but consistent) amount of time before
- a run.",
+ a run.",
},
:clientbucketdir => {
:default => "$vardir/clientbucket",
@@ -1185,8 +1272,8 @@ EOT
:default => "2m",
:type => :duration,
:desc => "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. #{AS_DURATION}",
+ before considering it a failure. This can help reduce flapping if too
+ many clients contact the server at one time. #{AS_DURATION}",
},
:report_server => {
:default => "$server",
@@ -1225,8 +1312,8 @@ EOT
:default => false,
:type => :boolean,
:desc => "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).",
+ configuration graphs. These dot files can be interpreted by tools
+ like OmniGraffle or dot (which is part of ImageMagick).",
},
:graphdir => {
:default => "$statedir/graphs",
@@ -1237,11 +1324,12 @@ EOT
:default => false,
:type => :boolean,
:desc => "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.",
+ 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.",
},
:waitforcert => {
:default => "2m",
@@ -1310,6 +1398,15 @@ EOT
is used for retrieval, so anything that is a valid file source can
be used here.",
},
+ :pluginfactdest => {
+ :type => :directory,
+ :default => "$vardir/facts.d",
+ :desc => "Where Puppet should store external facts that are being handled by pluginsync",
+ },
+ :pluginfactsource => {
+ :default => "puppet://$server/pluginfacts",
+ :desc => "Where to retrieve external facts for pluginsync",
+ },
:pluginsync => {
:default => true,
:type => :boolean,
@@ -1330,7 +1427,8 @@ EOT
:type => :path,
:default => "$vardir/lib/facter#{File::PATH_SEPARATOR}$vardir/facts",
:desc => "Where Puppet should look for facts. Multiple directories should
- be separated by the system path separator character. (The POSIX path separator is ':', and the Windows path separator is ';'.)",
+ be separated by the system path separator character. (The POSIX path
+ separator is ':', and the Windows path separator is ';'.)",
:call_hook => :on_initialize_and_write, # Call our hook with the default value, so we always get the value added to facter.
:hook => proc { |value| Facter.search(value) if Facter.respond_to?(:search) }}
@@ -1400,27 +1498,27 @@ EOT
:dbport => {
:default => "",
:desc => "The database password for caching. Only
- used when networked databases are used. #{STORECONFIGS_ONLY}",
+ used when networked databases are used. #{STORECONFIGS_ONLY}",
},
:dbuser => {
:default => "puppet",
:desc => "The database user for caching. Only
- used when networked databases are used. #{STORECONFIGS_ONLY}",
+ used when networked databases are used. #{STORECONFIGS_ONLY}",
},
:dbpassword => {
:default => "puppet",
:desc => "The database password for caching. Only
- used when networked databases are used. #{STORECONFIGS_ONLY}",
+ used when networked databases are used. #{STORECONFIGS_ONLY}",
},
:dbconnections => {
:default => '',
:desc => "The number of database connections for networked
- databases. Will be ignored unless the value is a positive integer. #{STORECONFIGS_ONLY}",
+ databases. Will be ignored unless the value is a positive integer. #{STORECONFIGS_ONLY}",
},
:dbsocket => {
:default => "",
:desc => "The database socket location. Only used when networked
- databases are used. Will be ignored if the value is an empty string. #{STORECONFIGS_ONLY}",
+ databases are used. Will be ignored if the value is an empty string. #{STORECONFIGS_ONLY}",
},
:railslog => {
:default => "$logdir/rails.log",
@@ -1434,8 +1532,8 @@ EOT
:rails_loglevel => {
:default => "info",
:desc => "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`. #{STORECONFIGS_ONLY}",
+ a valid log level within Rails. Production environments normally use `info`
+ and other environments normally use `debug`. #{STORECONFIGS_ONLY}",
}
)
@@ -1445,7 +1543,7 @@ EOT
:couchdb_url => {
:default => "http://127.0.0.1:5984/puppet",
:desc => "The url where the puppet couchdb database will be created.
- Only used when `facts_terminus` is set to `couch`.",
+ Only used when `facts_terminus` is set to `couch`.",
}
)
@@ -1454,15 +1552,15 @@ EOT
:tags => {
:default => "",
:desc => "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.",
+ only resources tagged with the specified tags will be applied.
+ Values must be comma-separated.",
},
:evaltrace => {
:default => false,
:type => :boolean,
:desc => "Whether each resource should log when it is
- being evaluated. This allows you to interactively see exactly
- what is being done.",
+ being evaluated. This allows you to interactively see exactly
+ what is being done.",
},
:summarize => {
:default => false,
@@ -1476,13 +1574,13 @@ EOT
:external_nodes => {
:default => "none",
:desc => "An external command that can produce node information. The command's output
- must be a YAML dump of a hash, and that hash must have a `classes` key and/or
- a `parameters` key, where `classes` is an array or hash and
- `parameters` is a hash. For unknown nodes, the command should
- exit with a non-zero exit code.
+ must be a YAML dump of a hash, and that hash must have a `classes` key and/or
+ a `parameters` key, where `classes` is an array or hash and
+ `parameters` is a hash. For unknown nodes, the command 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.",
+ This command makes it straightforward to store your node mapping
+ information in other data sources like databases.",
}
)
@@ -1492,15 +1590,15 @@ EOT
:default => false,
:type => :boolean,
:desc => "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.",
+ Defaults to false because SSL usually requires certificates
+ to be set up on the client side.",
},
:ldaptls => {
:default => false,
:type => :boolean,
:desc => "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.",
+ Defaults to false because TLS usually requires certificates
+ to be set up on the client side.",
},
:ldapserver => {
:default => "ldap",
@@ -1518,20 +1616,20 @@ EOT
:ldapclassattrs => {
:default => "puppetclass",
:desc => "The LDAP attributes to use to define Puppet classes. Values
- should be comma-separated.",
+ should be comma-separated.",
},
:ldapstackedattrs => {
:default => "puppetvar",
:desc => "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.",
+ the values in all hierarchy elements of the tree. Values
+ should be comma-separated.",
},
:ldapattrs => {
:default => "all",
:desc => "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.",
+ returned attributes are set as variables in the top-level scope.
+ Multiple values should be comma-separated. The value 'all' returns
+ all attributes.",
},
:ldapparentattr => {
:default => "parentnode",
@@ -1540,7 +1638,7 @@ EOT
:ldapuser => {
:default => "",
:desc => "The user to use to connect to LDAP. Must be specified as a
- full DN.",
+ full DN.",
},
:ldappassword => {
:default => "",
@@ -1549,9 +1647,9 @@ EOT
:ldapbase => {
:default => "",
:desc => "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.",
+ 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.",
}
)
@@ -1560,13 +1658,13 @@ EOT
:default => false,
:type => :boolean,
:desc => "Whether to store each client's configuration, including catalogs, facts,
-and related data. This also enables the import and export of resources in
-the Puppet language - a mechanism for exchange resources between nodes.
+ and related data. This also enables the import and export of resources in
+ the Puppet language - a mechanism for exchange resources between nodes.
-By default this uses ActiveRecord and an SQL database to store and query
-the data; this, in turn, will depend on Rails being available.
+ By default this uses ActiveRecord and an SQL database to store and query
+ the data; this, in turn, will depend on Rails being available.
-You can adjust the backend using the storeconfigs_backend setting.",
+ You can adjust the backend using the storeconfigs_backend setting.",
# Call our hook with the default value, so we always get the libdir set.
:call_hook => :on_initialize_and_write,
:hook => proc do |value|
@@ -1587,8 +1685,8 @@ You can adjust the backend using the storeconfigs_backend setting.",
:type => :terminus,
:default => "active_record",
:desc => "Configure the backend terminus used for StoreConfigs.
-By default, this uses the ActiveRecord store, which directly talks to the
-database from within the Puppet Master process."
+ By default, this uses the ActiveRecord store, which directly talks to the
+ database from within the Puppet Master process."
}
)
@@ -1597,57 +1695,63 @@ database from within the Puppet Master process."
:default => "$vardir/templates",
:type => :directory,
:desc => "Where Puppet looks for template files. Can be a list of colon-separated
- directories.",
+ directories.",
},
:allow_variables_with_dashes => {
:default => false,
:desc => <<-'EOT'
-Permit hyphens (`-`) in variable names and issue deprecation warnings about
-them. This setting **should always be `false`;** setting it to `true`
-will cause subtle and wide-ranging bugs. It will be removed in a future version.
-
-Hyphenated variables caused major problems in the language, but were allowed
-between Puppet 2.7.3 and 2.7.14. If you used them during this window, we
-apologize for the inconvenience --- you can temporarily set this to `true`
-in order to upgrade, and can rename your variables at your leisure. Please
-revert it to `false` after you have renamed all affected variables.
-EOT
+ Permit hyphens (`-`) in variable names and issue deprecation warnings about
+ them. This setting **should always be `false`;** setting it to `true`
+ will cause subtle and wide-ranging bugs. It will be removed in a future version.
+
+ Hyphenated variables caused major problems in the language, but were allowed
+ between Puppet 2.7.3 and 2.7.14. If you used them during this window, we
+ apologize for the inconvenience --- you can temporarily set this to `true`
+ in order to upgrade, and can rename your variables at your leisure. Please
+ revert it to `false` after you have renamed all affected variables.
+ EOT
},
:parser => {
:default => "current",
:desc => <<-'EOT'
-Selects the parser to use for parsing puppet manifests (in puppet DSL language/'.pp' files).
-Available choices are 'current' (the default), and 'future'.
+ Selects the parser to use for parsing puppet manifests (in puppet DSL
+ language/'.pp' files). Available choices are `current` (the default),
+ and `future`.
-The 'curent' parser means that the released version of the parser should be used.
+ The `curent` parser means that the released version of the parser should
+ be used.
-The 'future' parser is a "time travel to the future" allowing early exposure to new language features.
-What these fatures are will vary from release to release and they may be invididually configurable.
+ The `future` parser is a "time travel to the future" allowing early
+ exposure to new language features. What these fatures are will vary from
+ release to release and they may be invididually configurable.
-Available Since Puppet 3.2.
-EOT
+ Available Since Puppet 3.2.
+ EOT
},
:max_errors => {
:default => 10,
:desc => <<-'EOT'
-Sets the max number of logged/displayed parser validation errors in case multiple errors have been detected.
-A value of 0 is the same as value 1. The count is per manifest.
-EOT
+ Sets the max number of logged/displayed parser validation errors in case
+ multiple errors have been detected. A value of 0 is the same as value 1.
+ The count is per manifest.
+ EOT
},
:max_warnings => {
:default => 10,
:desc => <<-'EOT'
-Sets the max number of logged/displayed parser validation warnings in case multiple errors have been detected.
-A value of 0 is the same as value 1. The count is per manifest.
-EOT
+ Sets the max number of logged/displayed parser validation warnings in
+ case multiple errors have been detected. A value of 0 is the same as
+ value 1. The count is per manifest.
+ EOT
},
:max_deprecations => {
:default => 10,
:desc => <<-'EOT'
-Sets the max number of logged/displayed parser validation deprecation warnings in case multiple errors have been detected.
-A value of 0 is the same as value 1. The count is per manifest.
-EOT
+ Sets the max number of logged/displayed parser validation deprecation
+ warnings in case multiple errors have been detected. A value of 0 is the
+ same as value 1. The count is per manifest.
+ EOT
}
)
@@ -1656,7 +1760,7 @@ EOT
:default => false,
:type => :boolean,
:desc => "Whether to document all resources when using `puppet doc` to
- generate manifest documentation.",
+ generate manifest documentation.",
}
)
end
diff --git a/lib/puppet/error.rb b/lib/puppet/error.rb
index 15952662e..34da17219 100644
--- a/lib/puppet/error.rb
+++ b/lib/puppet/error.rb
@@ -56,5 +56,6 @@ module Puppet
# An error class for when I don't know what happened. Automatically
# prints a stack trace when in debug mode.
class DevError < Puppet::Error
+ include ExternalFileError
end
end
diff --git a/lib/puppet/external/lock.rb b/lib/puppet/external/lock.rb
deleted file mode 100644
index 024fedf3d..000000000
--- a/lib/puppet/external/lock.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-require 'thread'
-require 'sync'
-
-# Gotten from:
-# http://path.berkeley.edu/~vjoel/ruby/solaris-bug.rb
-
-# Extensions to the File class for exception-safe file locking in a
-# environment with multiple user threads.
-
-# This is here because closing a file on solaris unlocks any locks that
-# other threads might have. So we have to make sure that only the last
-# reader thread closes the file.
-#
-# The hash maps inode number to a count of reader threads
-$reader_count = Hash.new(0)
-
-class File
- # Get an exclusive (i.e., write) lock on the file, and yield to the block.
- # If the lock is not available, wait for it without blocking other ruby
- # threads.
- def lock_exclusive
- if Thread.list.size == 1
- flock(LOCK_EX)
- else
- # ugly hack because waiting for a lock in a Ruby thread blocks the
- # process
- period = 0.001
- until flock(LOCK_EX|LOCK_NB)
- sleep period
- period *= 2 if period < 1
- end
- end
-
- yield self
- ensure
- flush
- flock(LOCK_UN)
- end
-
- # Get a shared (i.e., read) lock on the file, and yield to the block.
- # If the lock is not available, wait for it without blocking other ruby
- # threads.
- def lock_shared
- if Thread.list.size == 1
- flock(LOCK_SH)
- else
- # ugly hack because waiting for a lock in a Ruby thread blocks the
- # process
- period = 0.001
- until flock(LOCK_SH|LOCK_NB)
- sleep period
- period *= 2 if period < 1
- end
- end
-
- yield self
- ensure
- Thread.exclusive {flock(LOCK_UN) if $reader_count[self.stat.ino] == 1}
- ## for solaris, no need to unlock here--closing does it
- ## but this has no effect on the bug
- end
-end
-
diff --git a/lib/puppet/external/nagios.rb b/lib/puppet/external/nagios.rb
index 2ed829198..582779441 100755..100644
--- a/lib/puppet/external/nagios.rb
+++ b/lib/puppet/external/nagios.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/env ruby -w
-
#--------------------
# A script to retrieve hosts from ldap and create an importable
# cfservd file from them
diff --git a/lib/puppet/external/nagios/base.rb b/lib/puppet/external/nagios/base.rb
index ce75f66ea..0aa50b411 100755..100644
--- a/lib/puppet/external/nagios/base.rb
+++ b/lib/puppet/external/nagios/base.rb
@@ -297,12 +297,13 @@ class Nagios::Base
def to_s
str = "define #{self.type} {\n"
- self.each { |param,value|
+ @parameters.keys.sort.each { |param|
+ value = @parameters[param]
str += %{\t%-30s %s\n} % [ param,
if value.is_a? Array
- value.join(",")
+ value.join(",").sub(';', '\;')
else
- value
+ value.sub(';', '\;')
end
]
}
diff --git a/lib/puppet/external/nagios/grammar.ry b/lib/puppet/external/nagios/grammar.ry
index 82f0a907c..d6bbc78ec 100644
--- a/lib/puppet/external/nagios/grammar.ry
+++ b/lib/puppet/external/nagios/grammar.ry
@@ -1,7 +1,7 @@
# vim: syntax=ruby
class Nagios::Parser
-token DEFINE NAME STRING PARAM LCURLY RCURLY VALUE RETURN COMMENT INLINECOMMENT
+token DEFINE NAME PARAM LCURLY RCURLY VALUE RETURN
rule
decls: decl { return val[0] if val[0] }
@@ -20,13 +20,9 @@ decls: decl { return val[0] if val[0] }
decl: object { result = [val[0]] }
| RETURN { result = nil }
- | comment
;
-comment: COMMENT RETURN { result = nil }
- ;
-
-object: DEFINE NAME LCURLY RETURN vars RCURLY {
+object: DEFINE NAME LCURLY returns vars RCURLY {
result = Nagios::Base.create(val[1],val[4])
}
;
@@ -40,146 +36,211 @@ vars: var
}
;
-var: PARAM VALUE icomment returns { result = {val[0] => val[1]} }
+var: PARAM VALUE returns { result = {val[0] => val[1]} }
;
-returns: RETURN
- | returns RETURN
- ;
-
-icomment: # nothing
- | INLINECOMMENT
+returns: RETURN
+ | RETURN returns
;
end
----inner
+require 'strscan'
class ::Nagios::Parser::SyntaxError < RuntimeError; end
def parse(src)
- @src = src
+ if src.respond_to?("force_encoding") then
+ src.force_encoding("ASCII-8BIT")
+ end
+ @ss = StringScanner.new(src)
- # state variables
- @invar = false
- @inobject = false
- @done = false
+ # state variables
+ @in_parameter_value = false
+ @in_object_definition = false
+ @done = false
- @line = 0
- @yydebug = true
+ @line = 1
+ @yydebug = true
- do_parse
+ do_parse
end
-# The lexer. Very simple.
-def token
- @src.sub!(/\A\n/,'')
- if $&
- @line += 1
- return [ :RETURN, "\n" ]
- end
+# This tokenizes the outside of object definitions,
+# and detects when we start defining an object.
+# We ignore whitespaces, comments and inline comments.
+# We yield when finding newlines, the "define" keyword,
+# the object name and the opening curly bracket.
+def tokenize_outside_definitions
+ case
+ when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
+ ;
- if @done
- return nil
- end
- yytext = String.new
+ when (text = @ss.scan(/\#.*$/)) # ignore comments
+ ;
+ when (text = @ss.scan(/;.*$/)) # ignore inline comments
+ ;
- # remove comments from this line
- @src.sub!(/\A[ \t]*;.*\n/,"\n")
- if $&
- return [:INLINECOMMENT, ""]
- end
+ when (text = @ss.scan(/\n/)) # newline
+ [:RETURN, text]
- @src.sub!(/\A#.*\n/,"\n")
- if $&
- return [:COMMENT, ""]
- end
+ when (text = @ss.scan(/\b(define)\b/)) # the "define" keyword
+ [:DEFINE, text]
- @src.sub!(/#.*/,'')
+ when (text = @ss.scan(/[^{ \t\n]+/)) # the name of the object being defined (everything not an opening curly bracket or a separator)
+ [:NAME, text]
- if @src.length == 0
- @done = true
- return [false, '$']
- end
+ when (text = @ss.scan(/\{/)) # the opening curly bracket - we enter object definition
+ @in_object_definition = true
+ [:LCURLY, text]
- if @invar
- @src.sub!(/\A[ \t]+/,'')
- @src.sub!(/\A([^;\n]+)(\n|;)/,'\2')
- if $1
- yytext += $1
- end
- @invar = false
- return [:VALUE, yytext]
- else
- @src.sub!(/\A[\t ]*(\S+)([\t ]*|$)/,'')
- if $1
- yytext = $1
- case yytext
- when 'define'
- #puts "got define"
- return [:DEFINE, yytext]
- when '{'
- #puts "got {"
- @inobject = true
- return [:LCURLY, yytext]
- else
- unless @inobject
- #puts "got type: #{yytext}"
- if yytext =~ /\W/
- giveback = yytext.dup
- giveback.sub!(/^\w+/,'')
- #puts "giveback " + giveback
- #puts "yytext " + yytext
- yytext.sub!(/\W.*$/,'')
- #puts "yytext " + yytext
- #puts "all [#{giveback} #{yytext} #{orig}]"
- @src = giveback + @src
- end
- return [:NAME, yytext]
- else
- if yytext == '}'
- #puts "got closure: #{yytext}"
- @inobject = false
- return [:RCURLY, '}']
- end
-
- unless @invar
- @invar = true
- return [:PARAM, $1]
- else
- end
- end
- end
- end
+ else
+ text = @ss.string[@ss.pos .. -1]
+ raise ScanError, "can not match: '#{text}'"
+ end # case
+end
+
+# This tokenizes until we find the parameter name.
+def tokenize_parameter_name
+ case
+ when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
+ ;
+
+ when (text = @ss.scan(/\#.*$/)) # ignore comments
+ ;
+
+ when (text = @ss.scan(/;.*$/)) # ignore inline comments
+ ;
+
+ when (text = @ss.scan(/\n/)) # newline
+ [:RETURN, text]
+
+ when (text = @ss.scan(/\}/)) # closing curly bracket : end of definition
+ @in_object_definition = false
+ [:RCURLY, text]
+
+ when (not @in_parameter_value and (text = @ss.scan(/\S+/))) # This is the name of the parameter
+ @in_parameter_value = true
+ [:PARAM, text]
+
+ else
+ text = @ss.string[@ss.pos .. -1]
+ raise ScanError, "can not match: '#{text}'"
+ end # case
+end
+
+# This tokenizes the parameter value.
+# There is a special handling for lines containing semicolons :
+# - unescaped semicolons are line comments (and should stop parsing of the line)
+# - escaped (with backslash \) semicolons should be kept in the parameter value (without the backslash)
+def tokenize_parameter_value
+ case
+ when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
+ ;
+
+ when (text = @ss.scan(/\#.*$/)) # ignore comments
+ ;
+
+ when (text = @ss.scan(/\n/)) # newline
+ [:RETURN, text]
+
+ when (text = @ss.scan(/.+$/)) # Value of parameter
+ @in_parameter_value = false
+
+ # Special handling of inline comments (;) and escaped semicolons (\;)
+
+ # We split the string on escaped semicolons (\;),
+ # Then we rebuild it as long as there are no inline comments (;)
+ # We join the rebuilt string with unescaped semicolons (on purpose)
+ array = text.split('\;', 0)
+
+ text = ""
+
+ array.each do |elt|
+
+ # Now we split at inline comments. If we have more than 1 element in the array
+ # it means we have an inline comment, so we are able to stop parsing
+ # However we still want to reconstruct the string with its first part (before the comment)
+ linearray = elt.split(';', 0)
+
+ # Let's reconstruct the string with a (unescaped) semicolon
+ if text != "" then
+ text += ';'
+ end
+ text += linearray[0]
+
+ # Now we can stop
+ if linearray.length > 1 then
+ break
+ end
end
+
+
+ # We strip the text to remove spaces between end of string and beginning of inline comment
+ [:VALUE, text.strip]
+
+ else
+ text = @ss.string[@ss.pos .. -1]
+ raise ScanError, "can not match: '#{text}'"
+ end # case
+end
+
+# This tokenizes inside an object definition.
+# Two cases : parameter name and parameter value
+def tokenize_inside_definitions
+ if @in_parameter_value
+ tokenize_parameter_value
+ else
+ tokenize_parameter_name
+ end
+end
+
+# The lexer. Very simple.
+def token
+ text = @ss.peek(1)
+ @line += 1 if text == "\n"
+
+ token = if @in_object_definition
+ tokenize_inside_definitions
+ else
+ tokenize_outside_definitions
+ end
+ token
end
def next_token
- token
+ return if @ss.eos?
+
+ # skips empty actions
+ until _next_token = token or @ss.eos?; end
+ _next_token
end
def yydebug
- 1
+ 1
end
def yywrap
- 0
+ 0
end
def on_error(token, value, vstack )
- msg = ""
- unless value.nil?
- msg = "line #{@line}: syntax error at '#{value}'"
- else
- msg = "line #{@line}: syntax error at '#{token}'"
- end
- unless @src.size > 0
- msg = "line #{@line}: Unexpected end of file"
- end
- if token == '$end'.intern
- puts "okay, this is silly"
- else
- raise ::Nagios::Parser::SyntaxError, msg
- end
+ # text = @ss.string[@ss.pos .. -1]
+ text = @ss.peek(20)
+ msg = ""
+ unless value.nil?
+ msg = "line #{@line}: syntax error at value '#{value}' : #{text}"
+ else
+ msg = "line #{@line}: syntax error at token '#{token}' : #{text}"
+ end
+ if @ss.eos?
+ msg = "line #{@line}: Unexpected end of file"
+ end
+ if token == '$end'.intern
+ puts "okay, this is silly"
+ else
+ raise ::Nagios::Parser::SyntaxError, msg
+ end
end
diff --git a/lib/puppet/external/nagios/parser.rb b/lib/puppet/external/nagios/parser.rb
index e89192b3a..9a82ffdb1 100644
--- a/lib/puppet/external/nagios/parser.rb
+++ b/lib/puppet/external/nagios/parser.rb
@@ -8,205 +8,269 @@ require 'racc/parser.rb'
module Nagios
class Parser < Racc::Parser
-module_eval(<<'...end grammar.ry/module_eval...', 'grammar.ry', 57)
+module_eval(<<'...end grammar.ry/module_eval...', 'grammar.ry', 49)
+require 'strscan'
class ::Nagios::Parser::SyntaxError < RuntimeError; end
def parse(src)
- @src = src
+ if src.respond_to?("force_encoding") then
+ src.force_encoding("ASCII-8BIT")
+ end
+ @ss = StringScanner.new(src)
- # state variables
- @invar = false
- @inobject = false
- @done = false
+ # state variables
+ @in_parameter_value = false
+ @in_object_definition = false
+ @done = false
- @line = 0
- @yydebug = true
+ @line = 1
+ @yydebug = true
- do_parse
+ do_parse
end
-# The lexer. Very simple.
-def token
- @src.sub!(/\A\n/,'')
- if $&
- @line += 1
- return [ :RETURN, "\n" ]
- end
+# This tokenizes the outside of object definitions,
+# and detects when we start defining an object.
+# We ignore whitespaces, comments and inline comments.
+# We yield when finding newlines, the "define" keyword,
+# the object name and the opening curly bracket.
+def tokenize_outside_definitions
+ case
+ when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
+ ;
- if @done
- return nil
- end
- yytext = String.new
+ when (text = @ss.scan(/\#.*$/)) # ignore comments
+ ;
+ when (text = @ss.scan(/;.*$/)) # ignore inline comments
+ ;
- # remove comments from this line
- @src.sub!(/\A[ \t]*;.*\n/,"\n")
- if $&
- return [:INLINECOMMENT, ""]
- end
+ when (text = @ss.scan(/\n/)) # newline
+ [:RETURN, text]
- @src.sub!(/\A#.*\n/,"\n")
- if $&
- return [:COMMENT, ""]
- end
+ when (text = @ss.scan(/\b(define)\b/)) # the "define" keyword
+ [:DEFINE, text]
- @src.sub!(/#.*/,'')
+ when (text = @ss.scan(/[^{ \t\n]+/)) # the name of the object being defined (everything not an opening curly bracket or a separator)
+ [:NAME, text]
- if @src.length == 0
- @done = true
- return [false, '$']
- end
+ when (text = @ss.scan(/\{/)) # the opening curly bracket - we enter object definition
+ @in_object_definition = true
+ [:LCURLY, text]
- if @invar
- @src.sub!(/\A[ \t]+/,'')
- @src.sub!(/\A([^;\n]+)(\n|;)/,'\2')
- if $1
- yytext += $1
- end
- @invar = false
- return [:VALUE, yytext]
- else
- @src.sub!(/\A[\t ]*(\S+)([\t ]*|$)/,'')
- if $1
- yytext = $1
- case yytext
- when 'define'
- #puts "got define"
- return [:DEFINE, yytext]
- when '{'
- #puts "got {"
- @inobject = true
- return [:LCURLY, yytext]
- else
- unless @inobject
- #puts "got type: #{yytext}"
- if yytext =~ /\W/
- giveback = yytext.dup
- giveback.sub!(/^\w+/,'')
- #puts "giveback " + giveback
- #puts "yytext " + yytext
- yytext.sub!(/\W.*$/,'')
- #puts "yytext " + yytext
- #puts "all [#{giveback} #{yytext} #{orig}]"
- @src = giveback + @src
- end
- return [:NAME, yytext]
- else
- if yytext == '}'
- #puts "got closure: #{yytext}"
- @inobject = false
- return [:RCURLY, '}']
- end
-
- unless @invar
- @invar = true
- return [:PARAM, $1]
- else
- end
- end
- end
- end
+ else
+ text = @ss.string[@ss.pos .. -1]
+ raise ScanError, "can not match: '#{text}'"
+ end # case
+end
+
+# This tokenizes until we find the parameter name.
+def tokenize_parameter_name
+ case
+ when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
+ ;
+
+ when (text = @ss.scan(/\#.*$/)) # ignore comments
+ ;
+
+ when (text = @ss.scan(/;.*$/)) # ignore inline comments
+ ;
+
+ when (text = @ss.scan(/\n/)) # newline
+ [:RETURN, text]
+
+ when (text = @ss.scan(/\}/)) # closing curly bracket : end of definition
+ @in_object_definition = false
+ [:RCURLY, text]
+
+ when (not @in_parameter_value and (text = @ss.scan(/\S+/))) # This is the name of the parameter
+ @in_parameter_value = true
+ [:PARAM, text]
+
+ else
+ text = @ss.string[@ss.pos .. -1]
+ raise ScanError, "can not match: '#{text}'"
+ end # case
+end
+
+# This tokenizes the parameter value.
+# There is a special handling for lines containing semicolons :
+# - unescaped semicolons are line comments (and should stop parsing of the line)
+# - escaped (with backslash \) semicolons should be kept in the parameter value (without the backslash)
+def tokenize_parameter_value
+ case
+ when (chars = @ss.skip(/[ \t]+/)) # ignore whitespace /\s+/
+ ;
+
+ when (text = @ss.scan(/\#.*$/)) # ignore comments
+ ;
+
+ when (text = @ss.scan(/\n/)) # newline
+ [:RETURN, text]
+
+ when (text = @ss.scan(/.+$/)) # Value of parameter
+ @in_parameter_value = false
+
+ # Special handling of inline comments (;) and escaped semicolons (\;)
+
+ # We split the string on escaped semicolons (\;),
+ # Then we rebuild it as long as there are no inline comments (;)
+ # We join the rebuilt string with unescaped semicolons (on purpose)
+ array = text.split('\;', 0)
+
+ text = ""
+
+ array.each do |elt|
+
+ # Now we split at inline comments. If we have more than 1 element in the array
+ # it means we have an inline comment, so we are able to stop parsing
+ # However we still want to reconstruct the string with its first part (before the comment)
+ linearray = elt.split(';', 0)
+
+ # Let's reconstruct the string with a (unescaped) semicolon
+ if text != "" then
+ text += ';'
+ end
+ text += linearray[0]
+
+ # Now we can stop
+ if linearray.length > 1 then
+ break
+ end
end
+
+
+ # We strip the text to remove spaces between end of string and beginning of inline comment
+ [:VALUE, text.strip]
+
+ else
+ text = @ss.string[@ss.pos .. -1]
+ raise ScanError, "can not match: '#{text}'"
+ end # case
+end
+
+# This tokenizes inside an object definition.
+# Two cases : parameter name and parameter value
+def tokenize_inside_definitions
+ if @in_parameter_value
+ tokenize_parameter_value
+ else
+ tokenize_parameter_name
+ end
+end
+
+# The lexer. Very simple.
+def token
+ text = @ss.peek(1)
+ @line += 1 if text == "\n"
+
+ token = if @in_object_definition
+ tokenize_inside_definitions
+ else
+ tokenize_outside_definitions
+ end
+ token
end
def next_token
- token
+ return if @ss.eos?
+
+ # skips empty actions
+ until _next_token = token or @ss.eos?; end
+ _next_token
end
def yydebug
- 1
+ 1
end
def yywrap
- 0
+ 0
end
def on_error(token, value, vstack )
- msg = ""
- unless value.nil?
- msg = "line #{@line}: syntax error at '#{value}'"
- else
- msg = "line #{@line}: syntax error at '#{token}'"
- end
- unless @src.size > 0
- msg = "line #{@line}: Unexpected end of file"
- end
- if token == '$end'.intern
- puts "okay, this is silly"
- else
- raise ::Nagios::Parser::SyntaxError, msg
- end
+ # text = @ss.string[@ss.pos .. -1]
+ text = @ss.peek(20)
+ msg = ""
+ unless value.nil?
+ msg = "line #{@line}: syntax error at value '#{value}' : #{text}"
+ else
+ msg = "line #{@line}: syntax error at token '#{token}' : #{text}"
+ end
+ if @ss.eos?
+ msg = "line #{@line}: Unexpected end of file"
+ end
+ if token == '$end'.intern
+ puts "okay, this is silly"
+ else
+ raise ::Nagios::Parser::SyntaxError, msg
+ end
end
...end grammar.ry/module_eval...
##### State transition tables begin ###
racc_action_table = [
- 8, 17, 7, 18, 7, 14, 12, 13, 11, 4,
- 6, 4, 6, 17, 10, 20, 22, 24, 25 ]
+ 8, 3, 3, 14, 12, 18, 10, 4, 4, 9,
+ 14, 12, 6, 19, 12 ]
racc_action_check = [
- 1, 15, 1, 15, 0, 13, 8, 11, 7, 1,
- 1, 0, 0, 14, 6, 17, 20, 21, 23 ]
+ 5, 0, 5, 13, 9, 13, 8, 0, 5, 6,
+ 11, 12, 3, 14, 19 ]
racc_action_pointer = [
- 2, 0, nil, nil, nil, nil, 5, 5, 6, nil,
- nil, 1, nil, -4, 8, -4, nil, 7, nil, nil,
- 5, 8, nil, 9, nil, nil ]
+ -1, nil, nil, 9, nil, 0, 4, nil, 6, -4,
+ nil, 6, 3, -1, 6, nil, nil, nil, nil, 6,
+ nil ]
racc_action_default = [
- -15, -15, -1, -3, -4, -5, -15, -15, -15, -2,
- -6, -15, 26, -15, -15, -15, -8, -15, -7, -9,
- -13, -15, -14, -10, -11, -12 ]
+ -11, -1, -3, -11, -4, -11, -11, -2, -11, -11,
+ 21, -11, -9, -11, -11, -6, -10, -7, -5, -11,
+ -8 ]
racc_goto_table = [
- 16, 19, 2, 9, 1, 15, 21, 23 ]
+ 11, 1, 15, 16, 17, 13, 7, 5, nil, nil,
+ 20 ]
racc_goto_check = [
- 6, 6, 2, 2, 1, 5, 7, 8 ]
+ 4, 2, 6, 4, 6, 5, 2, 1, nil, nil,
+ 4 ]
racc_goto_pointer = [
- nil, 4, 2, nil, nil, -9, -14, -14, -14 ]
+ nil, 7, 1, nil, -9, -6, -9 ]
racc_goto_default = [
- nil, nil, nil, 3, 5, nil, nil, nil, nil ]
+ nil, nil, nil, 2, nil, nil, nil ]
racc_reduce_table = [
0, 0, :racc_error,
- 1, 13, :_reduce_1,
- 2, 13, :_reduce_2,
- 1, 14, :_reduce_3,
- 1, 14, :_reduce_4,
+ 1, 10, :_reduce_1,
+ 2, 10, :_reduce_2,
+ 1, 11, :_reduce_3,
+ 1, 11, :_reduce_4,
+ 6, 12, :_reduce_5,
1, 14, :_reduce_none,
- 2, 16, :_reduce_6,
- 6, 15, :_reduce_7,
- 1, 17, :_reduce_none,
- 2, 17, :_reduce_9,
- 4, 18, :_reduce_10,
- 1, 20, :_reduce_none,
- 2, 20, :_reduce_none,
- 0, 19, :_reduce_none,
- 1, 19, :_reduce_none ]
+ 2, 14, :_reduce_7,
+ 3, 15, :_reduce_8,
+ 1, 13, :_reduce_none,
+ 2, 13, :_reduce_none ]
-racc_reduce_n = 15
+racc_reduce_n = 11
-racc_shift_n = 26
+racc_shift_n = 21
racc_token_table = {
false => 0,
:error => 1,
:DEFINE => 2,
:NAME => 3,
- :STRING => 4,
- :PARAM => 5,
- :LCURLY => 6,
- :RCURLY => 7,
- :VALUE => 8,
- :RETURN => 9,
- :COMMENT => 10,
- :INLINECOMMENT => 11 }
+ :PARAM => 4,
+ :LCURLY => 5,
+ :RCURLY => 6,
+ :VALUE => 7,
+ :RETURN => 8 }
-racc_nt_base = 12
+racc_nt_base = 9
racc_use_result_var = true
@@ -231,23 +295,18 @@ Racc_token_to_s_table = [
"error",
"DEFINE",
"NAME",
- "STRING",
"PARAM",
"LCURLY",
"RCURLY",
"VALUE",
"RETURN",
- "COMMENT",
- "INLINECOMMENT",
"$start",
"decls",
"decl",
"object",
- "comment",
+ "returns",
"vars",
- "var",
- "icomment",
- "returns" ]
+ "var" ]
Racc_debug_parser = false
@@ -257,82 +316,72 @@ Racc_debug_parser = false
module_eval(<<'.,.,', 'grammar.ry', 6)
def _reduce_1(val, _values, result)
- return val[0] if val[0]
+ return val[0] if val[0]
result
end
.,.,
module_eval(<<'.,.,', 'grammar.ry', 8)
def _reduce_2(val, _values, result)
- if val[1].nil?
- result = val[0]
- else
- if val[0].nil?
- result = val[1]
- else
- result = [ val[0], val[1] ].flatten
- end
- end
+ if val[1].nil?
+ result = val[0]
+ else
+ if val[0].nil?
+ result = val[1]
+ else
+ result = [ val[0], val[1] ].flatten
+ end
+ end
+
result
end
.,.,
module_eval(<<'.,.,', 'grammar.ry', 20)
def _reduce_3(val, _values, result)
- result = [val[0]]
+ result = [val[0]]
result
end
.,.,
module_eval(<<'.,.,', 'grammar.ry', 21)
def _reduce_4(val, _values, result)
- result = nil
+ result = nil
result
end
.,.,
-# reduce 5 omitted
-
module_eval(<<'.,.,', 'grammar.ry', 25)
- def _reduce_6(val, _values, result)
- result = nil
+ def _reduce_5(val, _values, result)
+ result = Nagios::Base.create(val[1],val[4])
+
result
end
.,.,
-module_eval(<<'.,.,', 'grammar.ry', 29)
- def _reduce_7(val, _values, result)
- result = Nagios::Base.create(val[1],val[4])
- result
- end
-.,.,
+# reduce 6 omitted
-# reduce 8 omitted
-
-module_eval(<<'.,.,', 'grammar.ry', 35)
- def _reduce_9(val, _values, result)
- val[1].each {|p,v|
- val[0][p] = v
- }
- result = val[0]
+module_eval(<<'.,.,', 'grammar.ry', 31)
+ def _reduce_7(val, _values, result)
+ val[1].each {|p,v|
+ val[0][p] = v
+ }
+ result = val[0]
+
result
end
.,.,
-module_eval(<<'.,.,', 'grammar.ry', 42)
- def _reduce_10(val, _values, result)
- result = {val[0] => val[1]}
+module_eval(<<'.,.,', 'grammar.ry', 38)
+ def _reduce_8(val, _values, result)
+ result = {val[0] => val[1]}
result
end
.,.,
-# reduce 11 omitted
-
-# reduce 12 omitted
-
-# reduce 13 omitted
+# reduce 9 omitted
-# reduce 14 omitted
+# reduce 10 omitted
def _reduce_none(val, _values, result)
val[0]
diff --git a/lib/puppet/external/pson/pure/parser.rb b/lib/puppet/external/pson/pure/parser.rb
index f14e6161c..bcdaf7b64 100644
--- a/lib/puppet/external/pson/pure/parser.rb
+++ b/lib/puppet/external/pson/pure/parser.rb
@@ -118,7 +118,7 @@ module PSON
else
raise TypeError, "#{source.inspect} is not like a string"
end
- if defined?(::Encoding)
+ if supports_encodings?(source)
if source.encoding == ::Encoding::ASCII_8BIT
b = source[0, 4].bytes.to_a
source =
@@ -157,6 +157,14 @@ module PSON
source
end
+ def supports_encodings?(string)
+ # Some modules, such as REXML on 1.8.7 (see #22804) can actually create
+ # a top-level Encoding constant when they are misused. Therefore
+ # checking for just that constant is not enough, so we'll be a bit more
+ # robust about if we can actually support encoding transformations.
+ string.respond_to?(:encoding) && defined?(::Encoding)
+ end
+
# Unescape characters in strings.
UNESCAPE_MAP = Hash.new { |h, k| h[k] = k.chr }
diff --git a/lib/puppet/face/file/store.rb b/lib/puppet/face/file/store.rb
index 6b145b8db..76ebbc15b 100644
--- a/lib/puppet/face/file/store.rb
+++ b/lib/puppet/face/file/store.rb
@@ -11,7 +11,7 @@ Puppet::Face.define(:file, '0.0.1') do
EOT
when_invoked do |path, options|
- file = Puppet::FileBucket::File.new(IO.binread(path))
+ file = Puppet::FileBucket::File.new(Puppet::FileSystem::File.new(path).binread)
Puppet::FileBucket::File.indirection.terminus_class = :file
Puppet::FileBucket::File.indirection.save file
diff --git a/lib/puppet/face/module/generate.rb b/lib/puppet/face/module/generate.rb
index a30a2ff59..fc9ca5f74 100644
--- a/lib/puppet/face/module/generate.rb
+++ b/lib/puppet/face/module/generate.rb
@@ -17,16 +17,14 @@ Puppet::Face.define(:module, '1.0.0') do
$ puppet module generate puppetlabs-ssh
notice: Generating module at /Users/kelseyhightower/puppetlabs-ssh
puppetlabs-ssh
- puppetlabs-ssh/tests
- puppetlabs-ssh/tests/init.pp
- puppetlabs-ssh/spec
- puppetlabs-ssh/spec/spec_helper.rb
- puppetlabs-ssh/spec/spec.opts
- puppetlabs-ssh/README
puppetlabs-ssh/Modulefile
- puppetlabs-ssh/metadata.json
+ puppetlabs-ssh/README
puppetlabs-ssh/manifests
puppetlabs-ssh/manifests/init.pp
+ puppetlabs-ssh/spec
+ puppetlabs-ssh/spec/spec_helper.rb
+ puppetlabs-ssh/tests
+ puppetlabs-ssh/tests/init.pp
EOT
arguments "<name>"
diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb
index 3242faca7..e42919091 100644
--- a/lib/puppet/face/parser.rb
+++ b/lib/puppet/face/parser.rb
@@ -35,17 +35,27 @@ Puppet::Face.define(:parser, '0.0.1') do
if files.empty?
if not STDIN.tty?
Puppet[:code] = STDIN.read
- Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types.clear
+ validate_manifest
else
files << Puppet[:manifest]
Puppet.notice "No manifest specified. Validating the default manifest #{Puppet[:manifest]}"
end
end
+ missing_files = []
files.each do |file|
+ missing_files << file if ! Puppet::FileSystem::File.exist?(file)
Puppet[:manifest] = file
- Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types.clear
+ validate_manifest
end
+ raise Puppet::Error, "One or more file(s) specified did not exist:\n#{missing_files.collect {|f| " " * 3 + f + "\n"}}" if ! missing_files.empty?
nil
end
end
+
+ def validate_manifest
+ Puppet::Node::Environment.new(Puppet[:environment]).known_resource_types.clear
+ rescue => detail
+ Puppet.log_exception(detail)
+ exit(1)
+ end
end
diff --git a/lib/puppet/face/plugin.rb b/lib/puppet/face/plugin.rb
index 76c79f38d..13ea32aa9 100644
--- a/lib/puppet/face/plugin.rb
+++ b/lib/puppet/face/plugin.rb
@@ -42,6 +42,12 @@ Puppet::Face.define(:plugin, '0.0.1') do
Puppet[:plugindest],
Puppet[:pluginsource],
Puppet[:pluginsignore]).evaluate
+ if Puppet.features.external_facts?
+ Puppet::Configurer::Downloader.new("pluginfacts",
+ Puppet[:pluginfactdest],
+ Puppet[:pluginfactsource],
+ Puppet[:pluginsignore]).evaluate
+ end
end
when_rendering :console do |value|
diff --git a/lib/puppet/feature/base.rb b/lib/puppet/feature/base.rb
index dcf454298..e30aed313 100644
--- a/lib/puppet/feature/base.rb
+++ b/lib/puppet/feature/base.rb
@@ -70,3 +70,19 @@ Puppet.features.add(:sqlite, :libs => ["sqlite3"])
Puppet.features.add(:hiera, :libs => ["hiera"])
Puppet.features.add(:minitar, :libs => ["archive/tar/minitar"])
+
+# We can manage symlinks
+Puppet.features.add(:manages_symlinks) do
+ if ! Puppet::Util::Platform.windows?
+ true
+ else
+ begin
+ require 'Win32API'
+ Win32API.new('kernel32', 'CreateSymbolicLink', 'SSL', 'B')
+ true
+ rescue LoadError => err
+ Puppet.debug("CreateSymbolicLink is not available")
+ false
+ end
+ end
+end
diff --git a/lib/puppet/feature/external_facts.rb b/lib/puppet/feature/external_facts.rb
new file mode 100644
index 000000000..1e2ad575e
--- /dev/null
+++ b/lib/puppet/feature/external_facts.rb
@@ -0,0 +1,5 @@
+require 'facter/util/config'
+
+Puppet.features.add(:external_facts) {
+ Facter::Util::Config.respond_to?(:external_facts_dirs=)
+}
diff --git a/lib/puppet/feature/libuser.rb b/lib/puppet/feature/libuser.rb
index bf34e20da..29a745de4 100644
--- a/lib/puppet/feature/libuser.rb
+++ b/lib/puppet/feature/libuser.rb
@@ -4,5 +4,5 @@ require 'puppet/util/libuser'
Puppet.features.add(:libuser) {
File.executable?("/usr/sbin/lgroupadd") and
File.executable?("/usr/sbin/luseradd") and
- File.exists?(Puppet::Util::Libuser.getconf)
+ Puppet::FileSystem::File.exist?(Puppet::Util::Libuser.getconf)
}
diff --git a/lib/puppet/feature/msgpack.rb b/lib/puppet/feature/msgpack.rb
new file mode 100644
index 000000000..8c7b7f963
--- /dev/null
+++ b/lib/puppet/feature/msgpack.rb
@@ -0,0 +1 @@
+Puppet.features.add(:msgpack, :libs => ["msgpack"])
diff --git a/lib/puppet/feature/rails.rb b/lib/puppet/feature/rails.rb
index 9e2ce4fe6..c1537effb 100644
--- a/lib/puppet/feature/rails.rb
+++ b/lib/puppet/feature/rails.rb
@@ -24,11 +24,11 @@ Puppet.features.add(:rails) do
require 'active_record'
require 'active_record/version'
rescue LoadError
- if FileTest.exists?("/usr/share/rails")
+ if Puppet::FileSystem::File.exist?("/usr/share/rails")
count = 0
Dir.entries("/usr/share/rails").each do |dir|
libdir = File.join("/usr/share/rails", dir, "lib")
- if FileTest.exists?(libdir) and ! $LOAD_PATH.include?(libdir)
+ if Puppet::FileSystem::File.exist?(libdir) and ! $LOAD_PATH.include?(libdir)
count += 1
$LOAD_PATH << libdir
end
diff --git a/lib/puppet/file_bucket/dipper.rb b/lib/puppet/file_bucket/dipper.rb
index c675f09a4..5d4d9382a 100644
--- a/lib/puppet/file_bucket/dipper.rb
+++ b/lib/puppet/file_bucket/dipper.rb
@@ -31,8 +31,9 @@ class Puppet::FileBucket::Dipper
# Back up a file to our bucket
def backup(file)
- raise(ArgumentError, "File #{file} does not exist") unless ::File.exist?(file)
- contents = IO.binread(file)
+ file_handle = Puppet::FileSystem::File.new(file)
+ raise(ArgumentError, "File #{file} does not exist") unless file_handle.exist?
+ contents = file_handle.binread
begin
file_bucket_file = Puppet::FileBucket::File.new(contents, :bucket_path => @local_path)
files_original_path = absolutize_path(file)
@@ -65,8 +66,9 @@ class Puppet::FileBucket::Dipper
# Restore the file
def restore(file,sum)
restore = true
- if FileTest.exists?(file)
- cursum = Digest::MD5.hexdigest(IO.binread(file))
+ file_handle = Puppet::FileSystem::File.new(file)
+ if file_handle.exist?
+ cursum = Digest::MD5.hexdigest(file_handle.binread)
# if the checksum has changed...
# this might be extra effort
@@ -79,8 +81,8 @@ class Puppet::FileBucket::Dipper
if newcontents = getfile(sum)
newsum = Digest::MD5.hexdigest(newcontents)
changed = nil
- if FileTest.exists?(file) and ! FileTest.writable?(file)
- changed = ::File.stat(file).mode
+ if file_handle.exist? and ! file_handle.writable?
+ changed = Puppet::FileSystem::File.new(file).stat.mode
::File.chmod(changed | 0200, file)
end
::File.open(file, ::File::WRONLY|::File::TRUNC|::File::CREAT) { |of|
diff --git a/lib/puppet/file_bucket/file.rb b/lib/puppet/file_bucket/file.rb
index 15e1cd76b..50e5d4d92 100644
--- a/lib/puppet/file_bucket/file.rb
+++ b/lib/puppet/file_bucket/file.rb
@@ -2,6 +2,7 @@ require 'puppet/file_bucket'
require 'puppet/indirector'
require 'puppet/util/checksums'
require 'digest/md5'
+require 'stringio'
class Puppet::FileBucket::File
# This class handles the abstract notion of a file in a filebucket.
@@ -34,6 +35,16 @@ class Puppet::FileBucket::File
raise ArgumentError.new("Unknown option(s): #{options.keys.join(', ')}") unless options.empty?
end
+ # @return [Num] The size of the contents
+ def size
+ contents.size
+ end
+
+ # @return [IO] A stream that reads the contents
+ def stream
+ StringIO.new(contents)
+ end
+
def checksum_type
'md5'
end
@@ -60,7 +71,11 @@ class Puppet::FileBucket::File
def to_pson
Puppet.deprecation_warning("Serializing Puppet::FileBucket::File objects to pson is deprecated.")
- { "contents" => contents }.to_pson
+ to_data_hash.to_pson
+ end
+
+ def to_data_hash
+ { "contents" => contents }
end
# This method is deprecated, but cannot be removed for awhile, otherwise
@@ -69,4 +84,5 @@ class Puppet::FileBucket::File
Puppet.deprecation_warning("Deserializing Puppet::FileBucket::File objects from pson is deprecated. Upgrade to a newer version.")
self.new(pson["contents"])
end
+
end
diff --git a/lib/puppet/file_serving/base.rb b/lib/puppet/file_serving/base.rb
index 93b5abee2..f04a7a453 100644
--- a/lib/puppet/file_serving/base.rb
+++ b/lib/puppet/file_serving/base.rb
@@ -21,11 +21,18 @@ class Puppet::FileServing::Base
# Return the full path to our file. Fails if there's no path set.
def full_path(dummy_argument=:work_arround_for_ruby_GC_bug)
- (if relative_path.nil? or relative_path == "" or relative_path == "."
- path
+ if relative_path.nil? or relative_path == "" or relative_path == "."
+ full_path = path
else
- File.join(path, relative_path)
- end).gsub(%r{//+}, "/")
+ full_path = File.join(path, relative_path)
+ end
+
+ if Puppet.features.microsoft_windows?
+ # Replace multiple slashes as long as they aren't at the beginning of a filename
+ full_path.gsub(%r{(./)/+}, '\1')
+ else
+ full_path.gsub(%r{//+}, '/')
+ end
end
def initialize(path, options = {})
@@ -61,17 +68,21 @@ class Puppet::FileServing::Base
# Stat our file, using the appropriate link-sensitive method.
def stat
@stat_method ||= self.links == :manage ? :lstat : :stat
- File.send(@stat_method, full_path)
+ Puppet::FileSystem::File.new(full_path).send(@stat_method)
+ end
+
+ def to_data_hash
+ {
+ 'path' => @path,
+ 'relative_path' => @relative_path,
+ 'links' => @links
+ }
end
def to_pson_data_hash
{
# No 'document_type' since we don't send these bare
- 'data' => {
- 'path' => @path,
- 'relative_path' => @relative_path,
- 'links' => @links
- },
+ 'data' => to_data_hash,
'metadata' => {
'api_version' => 1
}
diff --git a/lib/puppet/file_serving/configuration.rb b/lib/puppet/file_serving/configuration.rb
index d3ed15d61..c386a7e0a 100644
--- a/lib/puppet/file_serving/configuration.rb
+++ b/lib/puppet/file_serving/configuration.rb
@@ -1,20 +1,16 @@
-require 'monitor'
require 'puppet'
require 'puppet/file_serving'
require 'puppet/file_serving/mount'
require 'puppet/file_serving/mount/file'
require 'puppet/file_serving/mount/modules'
require 'puppet/file_serving/mount/plugins'
+require 'puppet/file_serving/mount/pluginfacts'
class Puppet::FileServing::Configuration
require 'puppet/file_serving/configuration/parser'
- extend MonitorMixin
-
def self.configuration
- synchronize do
- @configuration ||= new
- end
+ @configuration ||= new
end
Mount = Puppet::FileServing::Mount
@@ -84,13 +80,15 @@ class Puppet::FileServing::Configuration
@mounts["modules"].allow('*') if @mounts["modules"].empty?
@mounts["plugins"] ||= Mount::Plugins.new("plugins")
@mounts["plugins"].allow('*') if @mounts["plugins"].empty?
+ @mounts["pluginfacts"] ||= Mount::PluginFacts.new("pluginfacts")
+ @mounts["pluginfacts"].allow('*') if @mounts["pluginfacts"].empty?
end
# Read the configuration file.
def readconfig(check = true)
config = Puppet[:fileserverconfig]
- return unless FileTest.exists?(config)
+ return unless Puppet::FileSystem::File.exist?(config)
@parser ||= Puppet::FileServing::Configuration::Parser.new(config)
diff --git a/lib/puppet/file_serving/configuration/parser.rb b/lib/puppet/file_serving/configuration/parser.rb
index 3bd9c9ebd..aa09aab50 100644
--- a/lib/puppet/file_serving/configuration/parser.rb
+++ b/lib/puppet/file_serving/configuration/parser.rb
@@ -7,7 +7,7 @@ class Puppet::FileServing::Configuration::Parser
# Parse our configuration file.
def parse
- raise("File server configuration #{@file} does not exist") unless FileTest.exists?(@file)
+ raise("File server configuration #{@file} does not exist") unless Puppet::FileSystem::File.exist?(@file)
raise("Cannot read file server configuration #{@file}") unless FileTest.readable?(@file)
@mounts = {}
diff --git a/lib/puppet/file_serving/content.rb b/lib/puppet/file_serving/content.rb
index a6706bfcc..fc5a70ea5 100644
--- a/lib/puppet/file_serving/content.rb
+++ b/lib/puppet/file_serving/content.rb
@@ -34,7 +34,7 @@ class Puppet::FileServing::Content < Puppet::FileServing::Base
# This stat can raise an exception, too.
raise(ArgumentError, "Cannot read the contents of links unless following links") if stat.ftype == "symlink"
- @content = IO.binread(full_path)
+ @content = Puppet::FileSystem::File.new(full_path).binread
end
@content
end
diff --git a/lib/puppet/file_serving/fileset.rb b/lib/puppet/file_serving/fileset.rb
index 1360a7974..20957831f 100644
--- a/lib/puppet/file_serving/fileset.rb
+++ b/lib/puppet/file_serving/fileset.rb
@@ -77,7 +77,7 @@ class Puppet::FileServing::Fileset
links = links.to_sym
raise(ArgumentError, "Invalid :links value '#{links}'") unless [:manage, :follow].include?(links)
@links = links
- @stat_method = File.method(@links == :manage ? :lstat : :stat)
+ @stat_method = @links == :manage ? :lstat : :stat
end
private
@@ -133,7 +133,7 @@ class Puppet::FileServing::Fileset
end
def directory?
- stat_method.call(path).directory?
+ Puppet::FileSystem::File.new(path).send(stat_method).directory?
rescue Errno::ENOENT, Errno::EACCES
false
end
@@ -159,7 +159,7 @@ class Puppet::FileServing::Fileset
end
def valid?(path)
- @stat_method.call(path)
+ Puppet::FileSystem::File.new(path).send(@stat_method)
true
rescue Errno::ENOENT, Errno::EACCES
false
diff --git a/lib/puppet/file_serving/metadata.rb b/lib/puppet/file_serving/metadata.rb
index 877f5d258..2073c53ca 100644
--- a/lib/puppet/file_serving/metadata.rb
+++ b/lib/puppet/file_serving/metadata.rb
@@ -65,8 +65,8 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base
end
# Retrieve the attributes for this file, relative to a base directory.
- # Note that File.stat raises Errno::ENOENT if the file is absent and this
- # method does not catch that exception.
+ # Note that Puppet::FileSystem::File.new(path).stat raises Errno::ENOENT
+ # if the file is absent and this method does not catch that exception.
def collect
real_path = full_path
@@ -85,7 +85,7 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base
@checksum_type = "ctime"
@checksum = ("{#{@checksum_type}}") + send("#{@checksum_type}_file", path).to_s
when "link"
- @destination = File.readlink(real_path)
+ @destination = Puppet::FileSystem::File.new(real_path).readlink
@checksum = ("{#{@checksum_type}}") + send("#{@checksum_type}_file", real_path).to_s rescue nil
else
raise ArgumentError, "Cannot manage files of type #{stat.ftype}"
@@ -106,25 +106,29 @@ class Puppet::FileServing::Metadata < Puppet::FileServing::Base
super(path,data)
end
- PSON.register_document_type('FileMetadata',self)
- def to_pson_data_hash
- {
- 'document_type' => 'FileMetadata',
-
- 'data' => super['data'].update(
- {
- 'owner' => owner,
- 'group' => group,
- 'mode' => mode,
- 'checksum' => {
- 'type' => checksum_type,
- 'value' => checksum
+ def to_data_hash
+ super.update(
+ {
+ 'owner' => owner,
+ 'group' => group,
+ 'mode' => mode,
+ 'checksum' => {
+ 'type' => checksum_type,
+ 'value' => checksum
},
'type' => ftype,
'destination' => destination,
- }),
- 'metadata' => {
+ }
+ )
+ end
+
+ PSON.register_document_type('FileMetadata',self)
+ def to_pson_data_hash
+ {
+ 'document_type' => 'FileMetadata',
+ 'data' => to_data_hash,
+ 'metadata' => {
'api_version' => 1
}
}
diff --git a/lib/puppet/file_serving/mount/file.rb b/lib/puppet/file_serving/mount/file.rb
index 7f5af7f52..535a90bde 100644
--- a/lib/puppet/file_serving/mount/file.rb
+++ b/lib/puppet/file_serving/mount/file.rb
@@ -22,7 +22,7 @@ class Puppet::FileServing::Mount::File < Puppet::FileServing::Mount
file = ::File.join(full_path, relative_path)
- if !(FileTest.exist?(file) or FileTest.symlink?(file))
+ if !(Puppet::FileSystem::File.exist?(file) or Puppet::FileSystem::File.new(file).symlink?)
Puppet.info("File does not exist or is not accessible: #{file}")
return nil
end
diff --git a/lib/puppet/file_serving/mount/pluginfacts.rb b/lib/puppet/file_serving/mount/pluginfacts.rb
new file mode 100644
index 000000000..5ba8f7146
--- /dev/null
+++ b/lib/puppet/file_serving/mount/pluginfacts.rb
@@ -0,0 +1,35 @@
+require 'puppet/file_serving/mount'
+
+# Find files in the modules' pluginfacts directories.
+# This is a very strange mount because it merges
+# many directories into one.
+class Puppet::FileServing::Mount::PluginFacts < Puppet::FileServing::Mount
+ # Return an instance of the appropriate class.
+ def find(relative_path, request)
+ return nil unless mod = request.environment.modules.find { |mod| mod.pluginfact(relative_path) }
+
+ path = mod.pluginfact(relative_path)
+
+ path
+ end
+
+ def search(relative_path, request)
+ # We currently only support one kind of search on plugins - return
+ # them all.
+ Puppet.debug("Warning: calling Plugins.search with empty module path.") if request.environment.modules.empty?
+ paths = request.environment.modules.find_all { |mod| mod.pluginfacts? }.collect { |mod| mod.plugin_fact_directory }
+ if paths.empty?
+ # If the modulepath is valid then we still need to return a valid root
+ # directory for the search, but make sure nothing inside it is
+ # returned.
+ request.options[:recurse] = false
+ request.environment.modulepath.empty? ? nil : request.environment.modulepath
+ else
+ paths
+ end
+ end
+
+ def valid?
+ true
+ end
+end
diff --git a/lib/puppet/file_system.rb b/lib/puppet/file_system.rb
index d9e5b8fd3..78e0f71db 100644
--- a/lib/puppet/file_system.rb
+++ b/lib/puppet/file_system.rb
@@ -1,3 +1,6 @@
module Puppet::FileSystem
require 'puppet/file_system/path_pattern'
+ require 'puppet/file_system/file'
+ require 'puppet/file_system/memory_file'
+ require 'puppet/file_system/tempfile'
end
diff --git a/lib/puppet/file_system/file.rb b/lib/puppet/file_system/file.rb
new file mode 100644
index 000000000..d46b6846b
--- /dev/null
+++ b/lib/puppet/file_system/file.rb
@@ -0,0 +1,261 @@
+# An abstraction over the ruby file system operations for a single file.
+#
+# For the time being this is being kept private so that we can evolve it for a
+# while.
+#
+# @api private
+class Puppet::FileSystem::File
+ attr_reader :path
+
+ IMPL = if RUBY_VERSION =~ /^1\.8/
+ require 'puppet/file_system/file18'
+ Puppet::FileSystem::File18
+ elsif Puppet::Util::Platform.windows?
+ require 'puppet/file_system/file19windows'
+ Puppet::FileSystem::File19Windows
+ else
+ require 'puppet/file_system/file19'
+ Puppet::FileSystem::File19
+ end
+
+ @remembered = {}
+
+ def self.new(path)
+ if @remembered.include?(path.to_s)
+ @remembered[path.to_s]
+ else
+ file = IMPL.allocate
+ file.send(:initialize, path)
+ file
+ end
+ end
+
+ # Run a block of code with a file accessible in the filesystem.
+ # @note This API should only be used for testing
+ #
+ # @param file [Object] an object that conforms to the Puppet::FileSystem::File interface
+ # @api private
+ def self.overlay(file, &block)
+ remember(file)
+ yield
+ ensure
+ forget(file)
+ end
+
+ # Create a binding between a filename and a particular instance of a file object.
+ # @note This API should only be used for testing
+ #
+ # @param file [Object] an object that conforms to the Puppet::FileSystem::File interface
+ # @api private
+ def self.remember(file)
+ @remembered[file.path.to_s] = file
+ end
+
+ # Forget a remembered file
+ # @note This API should only be used for testing
+ #
+ # @param file [Object] an object that conforms to the Puppet::FileSystem::File interface
+ # @api private
+ def self.forget(file)
+ @remembered.delete(file.path.to_s)
+ end
+
+ def initialize(path)
+ if path.is_a?(Pathname)
+ @path = path
+ else
+ @path = Pathname.new(path)
+ end
+ end
+
+ def open(mode, options, &block)
+ ::File.open(@path, options, mode, &block)
+ end
+
+ # @return [Puppet::FileSystem::File] The directory of this file
+ # @api public
+ def dir
+ Puppet::FileSystem::File.new(@path.dirname)
+ end
+
+ # @return [Num] The size of this file
+ # @api public
+ def size
+ @path.size
+ end
+
+ # Allows exclusive updates to a file to be made by excluding concurrent
+ # access using flock. This means that if the file is on a filesystem that
+ # does not support flock, this method will provide no protection.
+ #
+ # While polling to aquire the lock the process will wait ever increasing
+ # amounts of time in order to prevent multiple processes from wasting
+ # resources.
+ #
+ # @param mode [Integer] The mode to apply to the file if it is created
+ # @param options [Integer] Extra file operation mode information to use
+ # (defaults to read-only mode)
+ # @param timeout [Integer] Number of seconds to wait for the lock (defaults to 300)
+ # @yield The file handle, in read-write mode
+ # @return [Void]
+ # @raise [Timeout::Error] If the timeout is exceeded while waiting to aquire the lock
+ # @api public
+ def exclusive_open(mode, options = 'r', timeout = 300, &block)
+ wait = 0.001 + (Kernel.rand / 1000)
+ written = false
+ while !written
+ ::File.open(@path, options, mode) do |rf|
+ if rf.flock(::File::LOCK_EX|::File::LOCK_NB)
+ yield rf
+ written = true
+ else
+ sleep wait
+ timeout -= wait
+ wait *= 2
+ if timeout < 0
+ raise Timeout::Error, "Timeout waiting for exclusive lock on #{@path}"
+ end
+ end
+ end
+ end
+ end
+
+ def each_line(&block)
+ ::File.open(@path) do |f|
+ f.each_line do |line|
+ yield line
+ end
+ end
+ end
+
+ # @return [String] The contents of the file
+ def read
+ @path.read
+ end
+
+ # @return [String] The binary contents of the file
+ def binread
+ raise NotImplementedError
+ end
+
+ # Determine if a file exists by verifying that the file can be stat'd.
+ # Will follow symlinks and verify that the actual target path exists.
+ #
+ # @return [Boolean] true if the named file exists.
+ def self.exist?(path)
+ return IMPL.exist?(path) if IMPL.method(:exist?) != self.method(:exist?)
+ File.exist?(path)
+ end
+
+ # Determine if a file exists by verifying that the file can be stat'd.
+ # Will follow symlinks and verify that the actual target path exists.
+ #
+ # @return [Boolean] true if the path of this file is present
+ def exist?
+ self.class.exist?(@path)
+ end
+
+ # Determine if a file is executable.
+ #
+ # @todo Should this take into account extensions on the windows platform?
+ #
+ # @return [Boolean] true if this file can be executed
+ def executable?
+ ::File.executable?(@path)
+ end
+
+ # @return [Boolean] Whether the file is writable by the current
+ # process
+ def writable?
+ @path.writable?
+ end
+
+ # Touches the file. On most systems this updates the mtime of the file.
+ def touch
+ ::FileUtils.touch(@path)
+ end
+
+ # Create the entire path as directories
+ def mkpath
+ @path.mkpath
+ end
+
+ # Creates a symbolic link dest which points to the current file.
+ # If dest already exists:
+ #
+ # * and is a file, will raise Errno::EEXIST
+ # * and is a directory, will return 0 but perform no action
+ # * and is a symlink referencing a file, will raise Errno::EEXIST
+ # * and is a symlink referencing a directory, will return 0 but perform no action
+ #
+ # With the :force option set to true, when dest already exists:
+ #
+ # * and is a file, will replace the existing file with a symlink (DANGEROUS)
+ # * and is a directory, will return 0 but perform no action
+ # * and is a symlink referencing a file, will modify the existing symlink
+ # * and is a symlink referencing a directory, will return 0 but perform no action
+ #
+ # @param dest [String] The path to create the new symlink at
+ # @param [Hash] options the options to create the symlink with
+ # @option options [Boolean] :force overwrite dest
+ # @option options [Boolean] :noop do not perform the operation
+ # @option options [Boolean] :verbose verbose output
+ #
+ # @raise [Errno::EEXIST] dest already exists as a file and, :force is not set
+ #
+ # @return [Integer] 0
+ def symlink(dest, options = {})
+ FileUtils.symlink(@path, dest, options)
+ end
+
+ # @return [Boolean] true if the file is a symbolic link.
+ def symlink?
+ File.symlink?(@path)
+ end
+
+ # @return [String] the name of the file referenced by the given link.
+ def readlink
+ File.readlink(@path)
+ end
+
+ # Deletes the named files, returning the number of names passed as arguments.
+ # See also Dir::rmdir.
+ #
+ # @raise an exception on any error.
+ #
+ # @return [Integer] the number of names passed as arguments
+ def self.unlink(*file_names)
+ return IMPL.unlink(*file_names) if IMPL.method(:unlink) != self.method(:unlink)
+ File.unlink(*file_names)
+ end
+
+ # Deletes the file.
+ # See also Dir::rmdir.
+ #
+ # @raise an exception on any error.
+ #
+ # @return [Integer] the number of names passed as arguments, in this case 1
+ def unlink
+ self.class.unlink(@path)
+ end
+
+ # @return [File::Stat] object for the named file.
+ def stat
+ File.stat(@path)
+ end
+
+ # @return [File::Stat] Same as stat, but does not follow the last symbolic
+ # link. Instead, reports on the link itself.
+ def lstat
+ File.lstat(@path)
+ end
+
+ # Compare the contents of this file against the contents of a stream.
+ # @param stream [IO] The stream to compare the contents against
+ # @return [Boolean] Whether the contents were the same
+ def compare_stream(stream)
+ open(0, 'rb') do |this|
+ FileUtils.compare_stream(this, stream)
+ end
+ end
+end
diff --git a/lib/puppet/file_system/file18.rb b/lib/puppet/file_system/file18.rb
new file mode 100644
index 000000000..99c2d5e06
--- /dev/null
+++ b/lib/puppet/file_system/file18.rb
@@ -0,0 +1,5 @@
+class Puppet::FileSystem::File18 < Puppet::FileSystem::File
+ def binread
+ ::File.open(@path, 'rb') { |f| f.read }
+ end
+end
diff --git a/lib/puppet/file_system/file19.rb b/lib/puppet/file_system/file19.rb
new file mode 100644
index 000000000..ca011613e
--- /dev/null
+++ b/lib/puppet/file_system/file19.rb
@@ -0,0 +1,5 @@
+class Puppet::FileSystem::File19 < Puppet::FileSystem::File
+ def binread
+ @path.binread
+ end
+end
diff --git a/lib/puppet/file_system/file19windows.rb b/lib/puppet/file_system/file19windows.rb
new file mode 100644
index 000000000..87f9915fe
--- /dev/null
+++ b/lib/puppet/file_system/file19windows.rb
@@ -0,0 +1,113 @@
+require 'puppet/file_system/file19'
+require 'puppet/util/windows'
+
+class Puppet::FileSystem::File19Windows < Puppet::FileSystem::File19
+
+ def self.exist?(path)
+ if ! Puppet.features.manages_symlinks?
+ return ::File.exist?(path)
+ end
+
+ path = path.to_str if path.respond_to?(:to_str) # support WatchedFile
+ path = path.to_s # support String and Pathname
+
+ begin
+ if Puppet::Util::Windows::File.symlink?(path)
+ path = Puppet::Util::Windows::File.readlink(path)
+ end
+ ! Puppet::Util::Windows::File.stat(path).nil?
+ rescue # generally INVALID_HANDLE_VALUE which means 'file not found'
+ false
+ end
+ end
+
+ def exist?
+ self.class.exist?(@path)
+ end
+
+ def symlink(dest, options = {})
+ raise_if_symlinks_unsupported
+
+ dest_exists = self.class.exist?(dest) # returns false on dangling symlink
+ dest_stat = Puppet::Util::Windows::File.stat(dest) if dest_exists
+ dest_symlink = Puppet::Util::Windows::File.symlink?(dest)
+
+ # silent fail to preserve semantics of original FileUtils
+ return 0 if dest_exists && dest_stat.ftype == 'directory'
+
+ if dest_exists && dest_stat.ftype == 'file' && options[:force] != true
+ raise(Errno::EEXIST, "#{dest} already exists and the :force option was not specified")
+ end
+
+ if options[:noop] != true
+ ::File.delete(dest) if dest_exists # can only be file
+ Puppet::Util::Windows::File.symlink(@path, dest)
+ end
+
+ 0
+ end
+
+ def symlink?
+ return false if ! Puppet.features.manages_symlinks?
+ Puppet::Util::Windows::File.symlink?(@path)
+ end
+
+ def readlink
+ raise_if_symlinks_unsupported
+ Puppet::Util::Windows::File.readlink(@path)
+ end
+
+ def self.unlink(*file_names)
+ if ! Puppet.features.manages_symlinks?
+ return ::File.unlink(*file_names)
+ end
+
+ file_names.each do |file_name|
+ file_name = file_name.to_s # handle PathName
+ stat = Puppet::Util::Windows::File.stat(file_name) rescue nil
+
+ # sigh, Ruby + Windows :(
+ if stat && stat.ftype == 'directory'
+ if Puppet::Util::Windows::File.symlink?(file_name)
+ Dir.rmdir(file_name)
+ else
+ raise Errno::EPERM.new(file_name)
+ end
+ else
+ ::File.unlink(file_name)
+ end
+ end
+
+ file_names.length
+ end
+
+ def unlink
+ self.class.unlink(@path)
+ end
+
+ def stat
+ if ! Puppet.features.manages_symlinks?
+ return super
+ end
+ Puppet::Util::Windows::File.stat(@path)
+ end
+
+ def lstat
+ if ! Puppet.features.manages_symlinks?
+ return Puppet::Util::Windows::File.stat(@path)
+ end
+ Puppet::Util::Windows::File.lstat(@path)
+ end
+
+ private
+ def raise_if_symlinks_unsupported
+ if ! Puppet.features.manages_symlinks?
+ msg = "This version of Windows does not support symlinks. Windows Vista / 2008 or higher is required."
+ raise Puppet::Util::Windows::Error.new(msg)
+ end
+
+ if ! Puppet::Util::Windows::Process.process_privilege_symlink?
+ Puppet.warning "The current user does not have the necessary permission to manage symlinks."
+ end
+ end
+end
diff --git a/lib/puppet/file_system/memory_file.rb b/lib/puppet/file_system/memory_file.rb
new file mode 100644
index 000000000..4605a7a01
--- /dev/null
+++ b/lib/puppet/file_system/memory_file.rb
@@ -0,0 +1,31 @@
+# An in-memory file abstraction. Commonly used with Puppet::FileSystem::File#overlay
+# @api private
+class Puppet::FileSystem::MemoryFile
+ attr_reader :path
+
+ def self.a_missing_file(path)
+ new(path, :exist? => false, :executable? => false)
+ end
+
+ def self.a_regular_file_containing(path, content)
+ new(path, :exist? => true, :executable? => false, :content => content)
+ end
+
+ def self.an_executable(path)
+ new(path, :exist? => true, :executable? => true)
+ end
+
+ def initialize(path, options)
+ @path = Pathname.new(path)
+ @exist = options[:exist?]
+ @executable = options[:executable?]
+ @content = options[:content]
+ end
+
+ def exist?; @exist; end
+ def executable?; @executable; end
+
+ def each_line(&block)
+ StringIO.new(@content).each_line(&block)
+ end
+end
diff --git a/lib/puppet/file_system/tempfile.rb b/lib/puppet/file_system/tempfile.rb
new file mode 100644
index 000000000..6766a7aec
--- /dev/null
+++ b/lib/puppet/file_system/tempfile.rb
@@ -0,0 +1,20 @@
+require 'tempfile'
+
+class Puppet::FileSystem::Tempfile
+
+ # Variation of Tempfile.open which ensures that the tempfile is closed and
+ # unlinked before returning
+ #
+ # @param identifier [String] additional part of generated pathname
+ # @yieldparam file [File] the temporary file object
+ # @return result of the passed block
+ # @api private
+ def self.open(identifier)
+ file = ::Tempfile.new(identifier)
+
+ yield file
+
+ ensure
+ file.close!
+ end
+end
diff --git a/lib/puppet/indirector/active_record.rb b/lib/puppet/indirector/active_record.rb
index a9f05d683..32c4dd4dc 100644
--- a/lib/puppet/indirector/active_record.rb
+++ b/lib/puppet/indirector/active_record.rb
@@ -1,3 +1,4 @@
+require 'puppet/rails'
require 'puppet/indirector'
class Puppet::Indirector::ActiveRecord < Puppet::Indirector::Terminus
diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb
index 9b1cc0305..b2a3c251a 100644
--- a/lib/puppet/indirector/catalog/compiler.rb
+++ b/lib/puppet/indirector/catalog/compiler.rb
@@ -41,6 +41,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
extract_facts_from_request(request)
node = node_from_request(request)
+ node.trusted_data = trusted_hash_from_request(request)
if catalog = compile(node)
return catalog
@@ -51,6 +52,7 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
end
end
+
# filter-out a catalog to remove exported resources
def filter(catalog)
return catalog.filter { |r| r.virtual? } if catalog.respond_to?(:filter)
@@ -70,6 +72,32 @@ class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code
private
+ # Produces a deeply frozen hash with trusted information
+ # The key :authenticated is always present in the result with one of the values
+ # :remote, :local, false, where :remote is authenticated via cert, :local is trusted by virtue
+ # of running on the same machine (not a remove request), and false is an unauthenticated remot request.
+ # When the trusted hash value for :authenticated == false, there is no other values set in the hash.
+ #
+ def trusted_hash_from_request(request)
+ if request.remote?
+ if request.authenticated?
+ trust_authenticated = 'remote'.freeze
+ client_cert = request.node
+ else
+ trust_authenticated = false
+ client_cert = nil
+ end
+ else
+ trust_authenticated = 'local'.freeze
+ # Always trust local data by picking up the available parameters.
+ request_node = request.options[:use_node]
+ client_cert = request_node ? request_node.parameters['clientcert'] : nil
+ end
+
+ # TODO nil or undef for client_cert missing?
+ trusted_hash = { 'authenticated' => trust_authenticated, 'certname' => client_cert }.freeze
+ end
+
# Add any extra data necessary to the node.
def add_node_data(node)
# Merge in our server-side facts, so they can be used during compilation.
diff --git a/lib/puppet/indirector/certificate_request/memory.rb b/lib/puppet/indirector/certificate_request/memory.rb
new file mode 100644
index 000000000..c60a1f9f1
--- /dev/null
+++ b/lib/puppet/indirector/certificate_request/memory.rb
@@ -0,0 +1,6 @@
+require 'puppet/ssl/certificate_request'
+require 'puppet/indirector/memory'
+
+class Puppet::SSL::CertificateRequest::Memory < Puppet::Indirector::Memory
+ desc "Store certificate requests in memory. This is used for testing puppet."
+end
diff --git a/lib/puppet/indirector/data_binding/hiera.rb b/lib/puppet/indirector/data_binding/hiera.rb
index 5271b997a..9ff640b2e 100644
--- a/lib/puppet/indirector/data_binding/hiera.rb
+++ b/lib/puppet/indirector/data_binding/hiera.rb
@@ -1,6 +1,50 @@
-require 'puppet/indirector/hiera'
+require 'puppet/indirector/code'
+require 'hiera/scope'
-class Puppet::DataBinding::Hiera < Puppet::Indirector::Hiera
+class Puppet::DataBinding::Hiera < Puppet::Indirector::Code
desc "Retrieve data using Hiera."
+
+ def initialize(*args)
+ if ! Puppet.features.hiera?
+ raise "Hiera terminus not supported without hiera library"
+ end
+ super
+ end
+
+ if defined?(::Psych::SyntaxError)
+ DataBindingExceptions = [::StandardError, ::Psych::SyntaxError]
+ else
+ DataBindingExceptions = [::StandardError]
+ end
+
+ def find(request)
+ hiera.lookup(request.key, nil, Hiera::Scope.new(request.options[:variables]), nil, nil)
+ rescue *DataBindingExceptions => detail
+ raise Puppet::DataBinding::LookupError.new(detail.message, detail)
+ end
+
+ private
+
+ def self.hiera_config
+ hiera_config = Puppet.settings[:hiera_config]
+ config = {}
+
+ if Puppet::FileSystem::File.exist?(hiera_config)
+ config = Hiera::Config.load(hiera_config)
+ else
+ Puppet.warning "Config file #{hiera_config} not found, using Hiera defaults"
+ end
+
+ config[:logger] = 'puppet'
+ config
+ end
+
+ def self.hiera
+ @hiera ||= Hiera.new(:config => hiera_config)
+ end
+
+ def hiera
+ self.class.hiera
+ end
end
diff --git a/lib/puppet/indirector/direct_file_server.rb b/lib/puppet/indirector/direct_file_server.rb
index 62234e360..dba4d60bd 100644
--- a/lib/puppet/indirector/direct_file_server.rb
+++ b/lib/puppet/indirector/direct_file_server.rb
@@ -6,14 +6,14 @@ class Puppet::Indirector::DirectFileServer < Puppet::Indirector::Terminus
include Puppet::FileServing::TerminusHelper
def find(request)
- return nil unless FileTest.exists?(request.key)
+ return nil unless Puppet::FileSystem::File.exist?(request.key)
instance = model.new(request.key)
instance.links = request.options[:links] if request.options[:links]
instance
end
def search(request)
- return nil unless FileTest.exists?(request.key)
+ return nil unless Puppet::FileSystem::File.exist?(request.key)
path2instances(request, request.key)
end
end
diff --git a/lib/puppet/indirector/facts/facter.rb b/lib/puppet/indirector/facts/facter.rb
index a4ee18ad3..4b44b5812 100644
--- a/lib/puppet/indirector/facts/facter.rb
+++ b/lib/puppet/indirector/facts/facter.rb
@@ -6,6 +6,8 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code
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."
+ private
+
def self.reload_facter
Facter.clear
Facter.loadfacts
@@ -24,6 +26,26 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code
end
end
+ def self.setup_external_facts(request)
+ # Add any per-module fact directories to the factpath
+ external_facts_dirs = []
+ request.environment.modules.each do |m|
+ if m.has_external_facts?
+ Puppet.info "Loading external facts from #{m.plugin_fact_directory}"
+ external_facts_dirs << m.plugin_fact_directory
+ end
+ end
+
+ # Add system external fact directory if it exists
+ if File.directory?(Puppet[:pluginfactdest])
+ external_facts_dirs << Puppet[:pluginfactdest]
+ end
+
+ # Add to facter config
+ Facter::Util::Config.external_facts_dirs += external_facts_dirs
+
+ end
+
def self.load_facts_in_dir(dir)
return unless FileTest.directory?(dir)
@@ -44,12 +66,15 @@ class Puppet::Node::Facts::Facter < Puppet::Indirector::Code
end
end
+ public
+
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)
+ self.class.setup_external_facts(request) if Puppet.features.external_facts?
self.class.reload_facter
self.class.load_fact_plugins
result = Puppet::Node::Facts.new(request.key, Facter.to_hash)
diff --git a/lib/puppet/indirector/file_bucket_file/file.rb b/lib/puppet/indirector/file_bucket_file/file.rb
index a8fd79b7e..59be12451 100644
--- a/lib/puppet/indirector/file_bucket_file/file.rb
+++ b/lib/puppet/indirector/file_bucket_file/file.rb
@@ -9,41 +9,40 @@ module Puppet::FileBucketFile
desc "Store files in a directory set based on their checksums."
- def initialize
- Puppet.settings.use(:filebucket)
- end
-
- def find( request )
- checksum, files_original_path = request_to_checksum_and_path( request )
- dir_path = path_for(request.options[:bucket_path], checksum)
- file_path = ::File.join(dir_path, 'contents')
-
- return nil unless ::File.exists?(file_path)
- return nil unless path_match(dir_path, files_original_path)
-
- if request.options[:diff_with]
- file2_path = path_for(request.options[:bucket_path], request.options[:diff_with], 'contents')
- raise "could not find diff_with #{request.options[:diff_with]}" unless ::File.exists?(file2_path)
- return `diff #{file_path.inspect} #{file2_path.inspect}`
+ def find(request)
+ checksum, files_original_path = request_to_checksum_and_path(request)
+ contents_file = path_for(request.options[:bucket_path], checksum, 'contents')
+ paths_file = path_for(request.options[:bucket_path], checksum, 'paths')
+
+ if contents_file.exist? && matches(paths_file, files_original_path)
+ if request.options[:diff_with]
+ other_contents_file = path_for(request.options[:bucket_path], request.options[:diff_with], 'contents')
+ raise "could not find diff_with #{request.options[:diff_with]}" unless other_contents_file.exist?
+ return `diff #{contents_file.path.to_s.inspect} #{other_contents_file.path.to_s.inspect}`
+ else
+ Puppet.info "FileBucket read #{checksum}"
+ model.new(contents_file.binread)
+ end
else
- contents = IO.binread(file_path)
- Puppet.info "FileBucket read #{checksum}"
- model.new(contents)
+ nil
end
end
def head(request)
checksum, files_original_path = request_to_checksum_and_path(request)
- dir_path = path_for(request.options[:bucket_path], checksum)
+ contents_file = path_for(request.options[:bucket_path], checksum, 'contents')
+ paths_file = path_for(request.options[:bucket_path], checksum, 'paths')
- ::File.exists?(::File.join(dir_path, 'contents')) and path_match(dir_path, files_original_path)
+ contents_file.exist? && matches(paths_file, files_original_path)
end
- def save( request )
+ def save(request)
instance = request.instance
- checksum, files_original_path = request_to_checksum_and_path(request)
+ _, files_original_path = request_to_checksum_and_path(request)
+ contents_file = path_for(instance.bucket_path, instance.checksum_data, 'contents')
+ paths_file = path_for(instance.bucket_path, instance.checksum_data, 'paths')
- save_to_disk(instance, files_original_path)
+ save_to_disk(instance, files_original_path, contents_file, paths_file)
# don't echo the request content back to the agent
model.new('')
@@ -55,57 +54,46 @@ module Puppet::FileBucketFile
private
- def path_match(dir_path, files_original_path)
+ def matches(paths_file, files_original_path)
+ paths_file.open(0640, 'a+') do |f|
+ path_match(f, files_original_path)
+ end
+ end
+
+ def path_match(file_handle, files_original_path)
return true unless files_original_path # if no path was provided, it's a match
- paths_path = ::File.join(dir_path, 'paths')
- return false unless ::File.exists?(paths_path)
- ::File.open(paths_path) do |f|
- f.each_line do |line|
- return true if line.chomp == files_original_path
- end
+ file_handle.rewind
+ file_handle.each_line do |line|
+ return true if line.chomp == files_original_path
end
return false
end
- def save_to_disk( bucket_file, files_original_path )
- filename = path_for(bucket_file.bucket_path, bucket_file.checksum_data, 'contents')
- dir_path = path_for(bucket_file.bucket_path, bucket_file.checksum_data)
- paths_path = ::File.join(dir_path, 'paths')
-
- # If the file already exists, touch it.
- if ::File.exist?(filename)
- verify_identical_file!(bucket_file)
- ::FileUtils.touch(filename)
- else
- # Make the directories if necessary.
- unless ::File.directory?(dir_path)
- Puppet::Util.withumask(0007) do
- ::FileUtils.mkdir_p(dir_path)
- end
+ def save_to_disk(bucket_file, files_original_path, contents_file, paths_file)
+ Puppet::Util.withumask(0007) do
+ unless paths_file.dir.exist?
+ paths_file.dir.mkpath
end
- Puppet.info "FileBucket adding #{bucket_file.checksum}"
-
- # Write the file to disk.
- Puppet::Util.withumask(0007) do
- ::File.open(filename, ::File::WRONLY|::File::CREAT, 0440) do |of|
- of.binmode
- of.print bucket_file.contents
+ paths_file.exclusive_open(0640, 'a+') do |f|
+ if contents_file.exist?
+ verify_identical_file!(contents_file, bucket_file)
+ contents_file.touch
+ else
+ contents_file.open(0440, 'wb') do |of|
+ of.write(bucket_file.contents)
+ end
end
- ::File.open(paths_path, ::File::WRONLY|::File::CREAT, 0640) do |of|
- # path will be written below
- end
- end
- end
- unless path_match(dir_path, files_original_path)
- ::File.open(paths_path, 'a') do |f|
- f.puts(files_original_path)
+ unless path_match(f, files_original_path)
+ f.seek(0, IO::SEEK_END)
+ f.puts(files_original_path)
+ end
end
end
end
- def request_to_checksum_and_path( request )
+ def request_to_checksum_and_path(request)
checksum_type, checksum, path = request.key.split(/\//, 3)
if path == '' # Treat "md5/<checksum>/" like "md5/<checksum>"
path = nil
@@ -121,22 +109,20 @@ module Puppet::FileBucketFile
dir = ::File.join(digest[0..7].split(""))
basedir = ::File.join(bucket_path, dir, digest)
- return basedir unless subfile
- ::File.join(basedir, subfile)
+ Puppet::FileSystem::File.new(subfile ? ::File.join(basedir, subfile) : basedir)
end
- # If conflict_check is enabled, verify that the passed text is
- # the same as the text in our file.
- def verify_identical_file!(bucket_file)
- disk_contents = IO.binread(path_for(bucket_file.bucket_path, bucket_file.checksum_data, 'contents'))
+ def verify_identical_file!(contents_file, bucket_file)
+ if bucket_file.contents.size == contents_file.size
+ if contents_file.compare_stream(bucket_file.stream)
+ Puppet.info "FileBucket got a duplicate file #{bucket_file.checksum}"
+ return
+ end
+ end
- # If the contents don't match, then we've found a conflict.
+ # If the contents or sizes don't match, then we've found a conflict.
# Unlikely, but quite bad.
- if disk_contents != bucket_file.contents
- raise Puppet::FileBucket::BucketError, "Got passed new contents for sum #{bucket_file.checksum}"
- else
- Puppet.info "FileBucket got a duplicate file #{bucket_file.checksum}"
- end
+ raise Puppet::FileBucket::BucketError, "Got passed new contents for sum #{bucket_file.checksum}"
end
end
end
diff --git a/lib/puppet/indirector/hiera.rb b/lib/puppet/indirector/hiera.rb
deleted file mode 100644
index 9cceb1da2..000000000
--- a/lib/puppet/indirector/hiera.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'puppet/indirector/terminus'
-
-class Puppet::Indirector::Hiera < Puppet::Indirector::Terminus
- def initialize(*args)
- if ! Puppet.features.hiera?
- raise "Hiera terminus not supported without hiera library"
- end
- super
- end
-
- def find(request)
- hiera.lookup(request.key, nil, request.options[:variables], nil, nil)
- end
-
- private
-
- def self.hiera_config
- hiera_config = Puppet.settings[:hiera_config]
- config = {}
-
- if File.exist?(hiera_config)
- config = Hiera::Config.load(hiera_config)
- else
- Puppet.warning "Config file #{hiera_config} not found, using Hiera defaults"
- end
-
- config[:logger] = 'puppet'
- config
- end
-
- def self.hiera
- @hiera ||= Hiera.new(:config => hiera_config)
- end
-
- def hiera
- self.class.hiera
- end
-end
-
diff --git a/lib/puppet/indirector/indirection.rb b/lib/puppet/indirector/indirection.rb
index a07bddcce..0c0cb2075 100644
--- a/lib/puppet/indirector/indirection.rb
+++ b/lib/puppet/indirector/indirection.rb
@@ -85,7 +85,7 @@ class Puppet::Indirector::Indirection
def doc
text = ""
- text += scrub(@doc) + "\n\n" if @doc
+ text << scrub(@doc) << "\n\n" if @doc
text << "* **Indirected Class**: `#{@indirected_class}`\n";
if terminus_setting
@@ -180,6 +180,10 @@ class Puppet::Indirector::Indirection
cache.save(request(:save, nil, instance, options))
end
+ def allow_remote_requests?
+ terminus.allow_remote_requests?
+ end
+
# Search for an instance in the appropriate terminus, caching the
# results if caching is configured..
def find(key, options={})
diff --git a/lib/puppet/indirector/json.rb b/lib/puppet/indirector/json.rb
index 1a8db8025..515ad33b6 100644
--- a/lib/puppet/indirector/json.rb
+++ b/lib/puppet/indirector/json.rb
@@ -21,7 +21,7 @@ class Puppet::Indirector::JSON < Puppet::Indirector::Terminus
end
def destroy(request)
- File.unlink(path(request.key))
+ Puppet::FileSystem::File.unlink(path(request.key))
rescue => detail
unless detail.is_a? Errno::ENOENT
raise Puppet::Error, "Could not destroy #{self.name} #{request.key}: #{detail}"
diff --git a/lib/puppet/indirector/key/ca.rb b/lib/puppet/indirector/key/ca.rb
index 056d037dd..d2c3482fb 100644
--- a/lib/puppet/indirector/key/ca.rb
+++ b/lib/puppet/indirector/key/ca.rb
@@ -9,4 +9,8 @@ class Puppet::SSL::Key::Ca < Puppet::Indirector::SslFile
store_in :privatekeydir
store_ca_at :cakey
+
+ def allow_remote_requests?
+ false
+ end
end
diff --git a/lib/puppet/indirector/key/file.rb b/lib/puppet/indirector/key/file.rb
index 1990f1a46..40f8a331d 100644
--- a/lib/puppet/indirector/key/file.rb
+++ b/lib/puppet/indirector/key/file.rb
@@ -7,6 +7,10 @@ class Puppet::SSL::Key::File < Puppet::Indirector::SslFile
store_in :privatekeydir
store_ca_at :cakey
+ def allow_remote_requests?
+ false
+ end
+
# Where should we store the public key?
def public_key_path(name)
if ca?(name)
@@ -20,10 +24,10 @@ class Puppet::SSL::Key::File < Puppet::Indirector::SslFile
def destroy(request)
super
- return unless FileTest.exist?(public_key_path(request.key))
+ return unless Puppet::FileSystem::File.exist?(public_key_path(request.key))
begin
- File.unlink(public_key_path(request.key))
+ Puppet::FileSystem::File.unlink(public_key_path(request.key))
rescue => detail
raise Puppet::Error, "Could not remove #{request.key} public key: #{detail}"
end
@@ -34,7 +38,7 @@ class Puppet::SSL::Key::File < Puppet::Indirector::SslFile
super
begin
- Puppet.settings.writesub(:publickeydir, public_key_path(request.key)) { |f| f.print request.instance.content.public_key.to_pem }
+ Puppet.settings.setting(:publickeydir).open_file(public_key_path(request.key), 'w') { |f| f.print request.instance.content.public_key.to_pem }
rescue => detail
raise Puppet::Error, "Could not write #{request.key}: #{detail}"
end
diff --git a/lib/puppet/indirector/key/memory.rb b/lib/puppet/indirector/key/memory.rb
new file mode 100644
index 000000000..527863e03
--- /dev/null
+++ b/lib/puppet/indirector/key/memory.rb
@@ -0,0 +1,6 @@
+require 'puppet/ssl/key'
+require 'puppet/indirector/memory'
+
+class Puppet::SSL::Key::Memory < Puppet::Indirector::Memory
+ desc "Store keys in memory. This is used for testing puppet."
+end
diff --git a/lib/puppet/indirector/node/write_only_yaml.rb b/lib/puppet/indirector/node/write_only_yaml.rb
index b30bdc1db..3f97e72e8 100644
--- a/lib/puppet/indirector/node/write_only_yaml.rb
+++ b/lib/puppet/indirector/node/write_only_yaml.rb
@@ -17,7 +17,7 @@ class Puppet::Node::WriteOnlyYaml < Puppet::Indirector::Yaml
# Overridden to always return nil. This is a write only terminus.
# @param [Object] request Ignored.
# @return [nil] This implementation always return nil'
- # @api
+ # @api public
def find(request)
nil
end
@@ -25,7 +25,7 @@ class Puppet::Node::WriteOnlyYaml < Puppet::Indirector::Yaml
# Overridden to always return nil. This is a write only terminus.
# @param [Object] request Ignored.
# @return [nil] This implementation always return nil
- # @api
+ # @api public
def search(request)
nil
end
diff --git a/lib/puppet/indirector/request.rb b/lib/puppet/indirector/request.rb
index 9f95b2f2e..4eacee11b 100644
--- a/lib/puppet/indirector/request.rb
+++ b/lib/puppet/indirector/request.rb
@@ -37,16 +37,12 @@ class Puppet::Indirector::Request
request
end
- def to_pson(*args)
+ def to_data_hash
result = {
- 'document_type' => 'IndirectorRequest',
- 'data' => {
- 'type' => indirection_name,
- 'method' => method,
- 'key' => key
- }
+ 'type' => indirection_name,
+ 'method' => method,
+ 'key' => key
}
- data = result['data']
attributes = {}
OPTION_ATTRIBUTES.each do |key|
next unless value = send(key)
@@ -57,10 +53,20 @@ class Puppet::Indirector::Request
attributes[opt] = value
end
- data['attributes'] = attributes unless attributes.empty?
- data['instance'] = instance if instance
+ result['attributes'] = attributes unless attributes.empty?
+ result['instance'] = instance if instance
+ result
+ end
- result.to_pson(*args)
+ def to_pson_data_hash
+ {
+ 'document_type' => 'IndirectorRequest',
+ 'data' => to_data_hash,
+ }
+ end
+
+ def to_pson(*args)
+ to_pson_data_hash.to_pson(*args)
end
# Is this an authenticated request?
diff --git a/lib/puppet/indirector/resource/ral.rb b/lib/puppet/indirector/resource/ral.rb
index 30c8623c5..5a366a329 100644
--- a/lib/puppet/indirector/resource/ral.rb
+++ b/lib/puppet/indirector/resource/ral.rb
@@ -5,6 +5,11 @@ class Puppet::Resource::Ral < Puppet::Indirector::Code
desc "Manipulate resources with the resource abstraction layer. Only used internally."
+ def allow_remote_requests?
+ Puppet.deprecation_warning("Accessing resources on the network is deprecated. See http://links.puppetlabs.com/deprecate-networked-resource")
+ super
+ end
+
def find( request )
# find by name
res = type(request).instances.find { |o| o.name == resource_name(request) }
diff --git a/lib/puppet/indirector/resource/rest.rb b/lib/puppet/indirector/resource/rest.rb
index 824af41d1..9992fc057 100644
--- a/lib/puppet/indirector/resource/rest.rb
+++ b/lib/puppet/indirector/resource/rest.rb
@@ -1,6 +1,7 @@
require 'puppet/indirector/status'
require 'puppet/indirector/rest'
+# @deprecated
class Puppet::Resource::Rest < Puppet::Indirector::REST
desc "Maniuplate resources remotely? Undocumented."
diff --git a/lib/puppet/indirector/resource/store_configs.rb b/lib/puppet/indirector/resource/store_configs.rb
index 45ffdbd5d..0c249d60b 100644
--- a/lib/puppet/indirector/resource/store_configs.rb
+++ b/lib/puppet/indirector/resource/store_configs.rb
@@ -6,4 +6,8 @@ class Puppet::Resource::StoreConfigs < Puppet::Indirector::StoreConfigs
desc %q{Part of the "storeconfigs" feature. Should not be directly set by end users.}
+ def allow_remote_requests?
+ Puppet.deprecation_warning("Accessing resources on the network is deprecated. See http://links.puppetlabs.com/deprecate-networked-resource")
+ super
+ end
end
diff --git a/lib/puppet/indirector/rest.rb b/lib/puppet/indirector/rest.rb
index 44c26e78b..d70b43bdf 100644
--- a/lib/puppet/indirector/rest.rb
+++ b/lib/puppet/indirector/rest.rb
@@ -55,7 +55,8 @@ class Puppet::Indirector::REST < Puppet::Indirector::Terminus
end
def network(request)
- Puppet::Network::HTTP::Connection.new(request.server || self.class.server, request.port || self.class.port)
+ Puppet::Network::HttpPool.http_instance(request.server || self.class.server,
+ request.port || self.class.port)
end
def http_get(request, path, headers = nil, *args)
diff --git a/lib/puppet/indirector/ssl_file.rb b/lib/puppet/indirector/ssl_file.rb
index df72ab4f5..a4ca4bd77 100644
--- a/lib/puppet/indirector/ssl_file.rb
+++ b/lib/puppet/indirector/ssl_file.rb
@@ -70,11 +70,11 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus
# Remove our file.
def destroy(request)
path = path(request.key)
- return false unless FileTest.exist?(path)
+ return false unless Puppet::FileSystem::File.exist?(path)
Puppet.notice "Removing file #{model} #{request.key} at '#{path}'"
begin
- File.unlink(path)
+ Puppet::FileSystem::File.unlink(path)
rescue => detail
raise Puppet::Error, "Could not remove #{request.key}: #{detail}"
end
@@ -135,10 +135,10 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus
# which we'll be EOL'ing at some point. This method was added at 20080702
# and should be removed at some point.
def rename_files_with_uppercase(file)
- return file if FileTest.exist?(file)
+ return file if Puppet::FileSystem::File.exist?(file)
dir, short = File.split(file)
- return nil unless FileTest.exist?(dir)
+ return nil unless Puppet::FileSystem::File.exist?(dir)
raise ArgumentError, "Tried to fix SSL files to a file containing uppercase" unless short.downcase == short
real_file = Dir.entries(dir).reject { |f| f =~ /^\./ }.find do |other|
@@ -159,12 +159,12 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus
# the work or opening a filehandle manually.
def write(name, path)
if ca?(name) and ca_location
- Puppet.settings.write(self.class.ca_setting) { |f| yield f }
+ Puppet.settings.setting(self.class.ca_setting).open('w') { |f| yield f }
elsif file_location
- Puppet.settings.write(self.class.file_setting) { |f| yield f }
+ Puppet.settings.setting(self.class.file_setting).open('w') { |f| yield f }
elsif setting = self.class.directory_setting
begin
- Puppet.settings.writesub(setting, path) { |f| yield f }
+ Puppet.settings.setting(setting).open_file(path, 'w') { |f| yield f }
rescue => detail
raise Puppet::Error, "Could not write #{path} to #{setting}: #{detail}"
end
diff --git a/lib/puppet/indirector/terminus.rb b/lib/puppet/indirector/terminus.rb
index 4e74efffd..b05ac3d8c 100644
--- a/lib/puppet/indirector/terminus.rb
+++ b/lib/puppet/indirector/terminus.rb
@@ -140,6 +140,10 @@ class Puppet::Indirector::Terminus
self.class.name
end
+ def allow_remote_requests?
+ true
+ end
+
def terminus_type
self.class.terminus_type
end
diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb
index 014994b9d..9c4e6f102 100644
--- a/lib/puppet/indirector/yaml.rb
+++ b/lib/puppet/indirector/yaml.rb
@@ -6,7 +6,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus
# Read a given name's file in and convert it from YAML.
def find(request)
file = path(request.key)
- return nil unless FileTest.exist?(file)
+ return nil unless Puppet::FileSystem::File.exist?(file)
begin
return Puppet::Util::Yaml.load_file(file)
@@ -24,7 +24,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus
basedir = File.dirname(file)
# This is quite likely a bad idea, since we're not managing ownership or modes.
- Dir.mkdir(basedir) unless FileTest.exist?(basedir)
+ Dir.mkdir(basedir) unless Puppet::FileSystem::File.exist?(basedir)
begin
Puppet::Util::Yaml.dump(request.instance, file)
@@ -46,7 +46,7 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus
def destroy(request)
file_path = path(request.key)
- File.unlink(file_path) if File.exists?(file_path)
+ Puppet::FileSystem::File.unlink(file_path) if Puppet::FileSystem::File.exist?(file_path)
end
def search(request)
diff --git a/lib/puppet/interface/documentation.rb b/lib/puppet/interface/documentation.rb
index ec312512e..d90a68a62 100644
--- a/lib/puppet/interface/documentation.rb
+++ b/lib/puppet/interface/documentation.rb
@@ -1,19 +1,12 @@
class Puppet::Interface
# @api private
module DocGen
+ require 'puppet/util/docs'
+
# @api private
def self.strip_whitespace(text)
- text.gsub!(/[ \t\f]+$/, '')
-
- # We need to identify an indent: the minimum number of whitespace
- # characters at the start of any line in the text.
- indent = text.split(/\n/).map {|x| x.index(/[^\s]/) }.compact.min
-
- if indent > 0 then
- text.gsub!(/^[ \t\f]{0,#{indent}}/, '')
- end
-
- return text
+ # I don't want no...
+ Puppet::Util::Docs.scrub(text)
end
# The documentation attributes all have some common behaviours; previously
diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb
index 8d5434edd..39274c044 100644
--- a/lib/puppet/module.rb
+++ b/lib/puppet/module.rb
@@ -20,6 +20,7 @@ class Puppet::Module
"files" => "files",
"templates" => "templates",
"plugins" => "lib",
+ "pluginfacts" => "facts.d",
}
# Find and return the +module+ that +path+ belongs to. If +path+ is
@@ -53,10 +54,14 @@ class Puppet::Module
def has_metadata?
return false unless metadata_file
- return false unless FileTest.exist?(metadata_file)
-
- metadata = PSON.parse File.read(metadata_file)
+ return false unless Puppet::FileSystem::File.exist?(metadata_file)
+ begin
+ metadata = PSON.parse(File.read(metadata_file))
+ rescue PSON::PSONError => e
+ Puppet.debug("#{name} has an invalid and unparsable metadata.json file. The parse error: #{e.message}")
+ return false
+ end
return metadata.is_a?(Hash) && !metadata.keys.empty?
end
@@ -66,7 +71,7 @@ class Puppet::Module
# we have files of a given type.
define_method(type +'?') do
type_subpath = subpath(location)
- unless FileTest.exist?(type_subpath)
+ unless Puppet::FileSystem::File.exist?(type_subpath)
Puppet.debug("No #{type} found in subpath '#{type_subpath}' " +
"(file / directory does not exist)")
return false
@@ -89,7 +94,7 @@ class Puppet::Module
full_path = subpath(location)
end
- return nil unless FileTest.exist?(full_path)
+ return nil unless Puppet::FileSystem::File.exist?(full_path)
return full_path
end
@@ -148,7 +153,7 @@ class Puppet::Module
end
def all_manifests
- return [] unless File.exists?(manifests)
+ return [] unless Puppet::FileSystem::File.exist?(manifests)
Dir.glob(File.join(manifests, '**', '*.{rb,pp}'))
end
@@ -169,6 +174,14 @@ class Puppet::Module
subpath("lib")
end
+ def plugin_fact_directory
+ subpath("facts.d")
+ end
+
+ def has_external_facts?
+ File.directory?(plugin_fact_directory)
+ end
+
def supports(name, version = nil)
@supports ||= []
@supports << [name, version]
diff --git a/lib/puppet/module_tool/applications/builder.rb b/lib/puppet/module_tool/applications/builder.rb
index d454a7bd4..65923c1d8 100644
--- a/lib/puppet/module_tool/applications/builder.rb
+++ b/lib/puppet/module_tool/applications/builder.rb
@@ -60,7 +60,7 @@ module Puppet::ModuleTool
when *Puppet::ModuleTool::ARTIFACTS
next
else
- FileUtils.cp_r path, build_path
+ FileUtils.cp_r path, build_path, :preserve => true
end
end
end
diff --git a/lib/puppet/module_tool/applications/installer.rb b/lib/puppet/module_tool/applications/installer.rb
index e53f6308b..772d7a831 100644
--- a/lib/puppet/module_tool/applications/installer.rb
+++ b/lib/puppet/module_tool/applications/installer.rb
@@ -32,7 +32,7 @@ module Puppet::ModuleTool
if is_module_package?(@name)
@source = :filesystem
@filename = File.expand_path(@name)
- raise MissingPackageError, :requested_package => @filename unless File.exist?(@filename)
+ raise MissingPackageError, :requested_package => @filename unless Puppet::FileSystem::File.exist?(@filename)
parsed = parse_filename(@filename)
@module_name = parsed[:module_name]
diff --git a/lib/puppet/module_tool/checksums.rb b/lib/puppet/module_tool/checksums.rb
index 0985b71e4..044357a8f 100644
--- a/lib/puppet/module_tool/checksums.rb
+++ b/lib/puppet/module_tool/checksums.rb
@@ -16,7 +16,7 @@ module Puppet::ModuleTool
# Return checksum for the +Pathname+.
def checksum(pathname)
- return Digest::MD5.hexdigest(IO.binread(pathname))
+ return Digest::MD5.hexdigest(Puppet::FileSystem::File.new(pathname).binread)
end
# Return checksums for object's +Pathname+, generate if it's needed.
diff --git a/lib/puppet/module_tool/dependency.rb b/lib/puppet/module_tool/dependency.rb
index 847a2e3c1..222e714c8 100644
--- a/lib/puppet/module_tool/dependency.rb
+++ b/lib/puppet/module_tool/dependency.rb
@@ -15,12 +15,16 @@ module Puppet::ModuleTool
@repository = repository ? Puppet::Forge::Repository.new(repository) : nil
end
- # Return PSON representation of this data.
- def to_pson(*args)
+ def to_data_hash
result = { :name => @full_module_name }
result[:version_requirement] = @version_requirement if @version_requirement && ! @version_requirement.nil?
result[:repository] = @repository.to_s if @repository && ! @repository.nil?
- result.to_pson(*args)
+ result
+ end
+
+ # Return PSON representation of this data.
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
end
end
diff --git a/lib/puppet/module_tool/metadata.rb b/lib/puppet/module_tool/metadata.rb
index 37a753626..650043802 100644
--- a/lib/puppet/module_tool/metadata.rb
+++ b/lib/puppet/module_tool/metadata.rb
@@ -129,7 +129,7 @@ module Puppet::ModuleTool
end
end
- def to_hash()
+ def to_data_hash()
return extra_metadata.merge({
'name' => @full_module_name,
'version' => @version,
@@ -145,9 +145,13 @@ module Puppet::ModuleTool
})
end
+ def to_hash()
+ to_data_hash
+ end
+
# Return the PSON record representing this instance.
def to_pson(*args)
- return to_hash.to_pson(*args)
+ return to_data_hash.to_pson(*args)
end
end
end
diff --git a/lib/puppet/module_tool/tar.rb b/lib/puppet/module_tool/tar.rb
index 4f9f87ed2..6b3257cf4 100644
--- a/lib/puppet/module_tool/tar.rb
+++ b/lib/puppet/module_tool/tar.rb
@@ -4,7 +4,8 @@ module Puppet::ModuleTool::Tar
require 'puppet/module_tool/tar/mini'
def self.instance(module_name)
- if Facter.value('osfamily') == 'Solaris' && Puppet::Util.which('gtar') && ! Puppet::Util::Platform.windows?
+ gtar_platforms = ['Solaris', 'OpenBSD']
+ if gtar_platforms.include?(Facter.value('osfamily')) && Puppet::Util.which('gtar')
Solaris.new
elsif Puppet::Util.which('tar') && ! Puppet::Util::Platform.windows?
Gnu.new
diff --git a/lib/puppet/module_tool/tar/gnu.rb b/lib/puppet/module_tool/tar/gnu.rb
index 0e663b7fa..d8fc3378f 100644
--- a/lib/puppet/module_tool/tar/gnu.rb
+++ b/lib/puppet/module_tool/tar/gnu.rb
@@ -1,8 +1,12 @@
class Puppet::ModuleTool::Tar::Gnu
+ def initialize(command = "tar")
+ @command = command
+ end
+
def unpack(sourcefile, destdir, owner)
- Puppet::Util::Execution.execute("tar xzf #{sourcefile} --no-same-permissions --no-same-owner -C #{destdir}")
+ Puppet::Util::Execution.execute("#{@command} xzf #{sourcefile} --no-same-owner -C #{destdir}")
Puppet::Util::Execution.execute("find #{destdir} -type d -exec chmod 755 {} +")
- Puppet::Util::Execution.execute("find #{destdir} -type f -exec chmod 644 {} +")
+ Puppet::Util::Execution.execute("find #{destdir} -type f -exec chmod a-wst {} +")
Puppet::Util::Execution.execute("chown -R #{owner} #{destdir}")
end
diff --git a/lib/puppet/module_tool/tar/mini.rb b/lib/puppet/module_tool/tar/mini.rb
index 8288876ae..f64577ea0 100644
--- a/lib/puppet/module_tool/tar/mini.rb
+++ b/lib/puppet/module_tool/tar/mini.rb
@@ -7,6 +7,8 @@ class Puppet::ModuleTool::Tar::Mini
Zlib::GzipReader.open(sourcefile) do |reader|
Archive::Tar::Minitar.unpack(reader, destdir) do |action, name, stats|
case action
+ when :file_done
+ File.chmod(0444, "#{destdir}/#{name}")
when :dir, :file_start
validate_entry(destdir, name)
Puppet.debug("extracting #{destdir}/#{name}")
diff --git a/lib/puppet/module_tool/tar/solaris.rb b/lib/puppet/module_tool/tar/solaris.rb
index c618fbf67..fb3e58bbf 100644
--- a/lib/puppet/module_tool/tar/solaris.rb
+++ b/lib/puppet/module_tool/tar/solaris.rb
@@ -1,8 +1,5 @@
class Puppet::ModuleTool::Tar::Solaris < Puppet::ModuleTool::Tar::Gnu
- def unpack(sourcefile, destdir, owner)
- Puppet::Util::Execution.execute("gtar xzf #{sourcefile} --no-same-permissions --no-same-owner -C #{destdir}")
- Puppet::Util::Execution.execute("find #{destdir} -type d -exec chmod 755 {} +")
- Puppet::Util::Execution.execute("find #{destdir} -type f -exec chmod 644 {} +")
- Puppet::Util::Execution.execute("chown -R #{owner} #{destdir}")
+ def initialize
+ super("gtar")
end
end
diff --git a/lib/puppet/network/authconfig.rb b/lib/puppet/network/authconfig.rb
index 1c3eaede6..527774598 100644
--- a/lib/puppet/network/authconfig.rb
+++ b/lib/puppet/network/authconfig.rb
@@ -3,8 +3,6 @@ require 'puppet/network/rights'
module Puppet
class ConfigurationError < Puppet::Error; end
class Network::AuthConfig
-
- extend MonitorMixin
attr_accessor :rights
DEFAULT_ACL = [
diff --git a/lib/puppet/network/authentication.rb b/lib/puppet/network/authentication.rb
index 91e16db72..f10e461d3 100644
--- a/lib/puppet/network/authentication.rb
+++ b/lib/puppet/network/authentication.rb
@@ -14,7 +14,7 @@ module Puppet::Network::Authentication
certs << Puppet::SSL::CertificateAuthority.instance.host.certificate if Puppet::SSL::CertificateAuthority.ca?
# Always check the host cert if we have one, this will be the agent or master cert depending on the run mode
- certs << Puppet::SSL::Host.localhost.certificate if FileTest.exist?(Puppet[:hostcert])
+ certs << Puppet::SSL::Host.localhost.certificate if Puppet::FileSystem::File.exist?(Puppet[:hostcert])
# Remove nil values for caller convenience
certs.compact.each do |cert|
diff --git a/lib/puppet/network/authstore.rb b/lib/puppet/network/authstore.rb
index 90498002d..ba86ed90b 100755..100644
--- a/lib/puppet/network/authstore.rb
+++ b/lib/puppet/network/authstore.rb
@@ -82,21 +82,20 @@ module Puppet
end
def interpolate(match)
- Thread.current[:declarations] = @declarations.collect { |ace| ace.interpolate(match) }.sort
+ @modified_declarations = @declarations.collect { |ace| ace.interpolate(match) }.sort
end
def reset_interpolation
- Thread.current[:declarations] = nil
+ @modified_declarations = nil
end
private
- # returns our ACEs list, but if we have a modification of it
- # in our current thread, let's return it
- # this is used if we want to override the this purely immutable list
- # by a modified version in a multithread safe way.
+ # Returns our ACEs list, but if we have a modification of it, let's return
+ # it. This is used if we want to override the this purely immutable list
+ # by a modified version.
def declarations
- Thread.current[:declarations] || @declarations
+ @modified_declarations || @declarations
end
# Store the results of a pattern into our hash. Basically just
diff --git a/lib/puppet/network/format.rb b/lib/puppet/network/format.rb
index 69895c344..e50dfd32a 100644
--- a/lib/puppet/network/format.rb
+++ b/lib/puppet/network/format.rb
@@ -1,10 +1,9 @@
-require 'puppet/provider'
-require 'puppet/provider/confiner'
+require 'puppet/confiner'
# A simple class for modeling encoding formats for moving
# instances around the network.
class Puppet::Network::Format
- include Puppet::Provider::Confiner
+ include Puppet::Confiner
attr_reader :name, :mime
attr_accessor :intern_method, :render_method, :intern_multiple_method, :render_multiple_method, :weight, :required_methods, :extension
diff --git a/lib/puppet/network/format_handler.rb b/lib/puppet/network/format_handler.rb
index 880b58b3c..d341b4335 100644
--- a/lib/puppet/network/format_handler.rb
+++ b/lib/puppet/network/format_handler.rb
@@ -5,6 +5,8 @@ require 'puppet/network/format'
module Puppet::Network::FormatHandler
class FormatError < Puppet::Error; end
+ ALL_MEDIA_TYPES = '*/*'.freeze
+
@formats = {}
def self.create(*args, &block)
@@ -70,24 +72,20 @@ module Puppet::Network::FormatHandler
# that generally conforms to an HTTP Accept header. Any quality specifiers
# are ignored and instead the formats are simply in strict preference order
# (most preferred is first)
- # @param supported [Array<Symbol>] the names of the supported formats (order
- # does not matter)
+ # @param supported [Array<Symbol>] the names of the supported formats (the
+ # most preferred format is first)
# @return [Puppet::Network::Format, nil] the most suitable format
# @api private
def self.most_suitable_format_for(accepted, supported)
format_name = accepted.collect do |accepted|
accepted.to_s.sub(/;q=.*$/, '')
end.collect do |accepted|
- begin
- if accepted == '*/*'
- formats
- else
- format_to_canonical_name(accepted)
- end
- rescue ArgumentError
- nil
+ if accepted == ALL_MEDIA_TYPES
+ supported.first
+ else
+ format_to_canonical_name_or_nil(accepted)
end
- end.flatten.find do |accepted|
+ end.find do |accepted|
supported.include?(accepted)
end
@@ -95,6 +93,13 @@ module Puppet::Network::FormatHandler
format_for(format_name)
end
end
+
+ # @api private
+ def self.format_to_canonical_name_or_nil(format)
+ format_to_canonical_name(format)
+ rescue ArgumentError
+ nil
+ end
end
require 'puppet/network/formats'
diff --git a/lib/puppet/network/format_support.rb b/lib/puppet/network/format_support.rb
index 7cc6cc001..79f7fe665 100644
--- a/lib/puppet/network/format_support.rb
+++ b/lib/puppet/network/format_support.rb
@@ -1,6 +1,7 @@
require 'puppet/network/format_handler'
# Provides network serialization support when included
+# @api public
module Puppet::Network::FormatSupport
def self.included(klass)
klass.extend(ClassMethods)
@@ -83,6 +84,10 @@ module Puppet::Network::FormatSupport
end
end
+ def to_msgpack(*args)
+ to_data_hash.to_msgpack(*args)
+ end
+
def render(format = nil)
format ||= self.class.default_format
@@ -102,5 +107,14 @@ module Puppet::Network::FormatSupport
def support_format?(name)
self.class.support_format?(name)
end
+
+ # @comment Document to_data_hash here as it is called as a hook from to_msgpack if it exists
+ # @!method to_data_hash(*args)
+ # @api public
+ # @abstract
+ # This method may be implemented to return a hash object that is used for serializing.
+ # The object returned by this method should contain all the info needed to instantiate it again.
+ # If the method exists it will be called from to_msgpack and other serialization methods.
+ # @return [Hash]
end
diff --git a/lib/puppet/network/formats.rb b/lib/puppet/network/formats.rb
index 4aaeddedd..62e40d376 100644
--- a/lib/puppet/network/formats.rb
+++ b/lib/puppet/network/formats.rb
@@ -1,5 +1,31 @@
require 'puppet/network/format_handler'
+Puppet::Network::FormatHandler.create_serialized_formats(:msgpack, :weight => 20, :mime => "application/x-msgpack", :required_methods => [:render_method, :intern_method]) do
+ def intern(klass, text)
+ data = MessagePack.unpack(text)
+ return data if data.is_a?(klass)
+ klass.from_pson(data)
+ end
+
+ def intern_multiple(klass, text)
+ MessagePack.unpack(text).collect do |data|
+ klass.from_pson(data)
+ end
+ end
+
+ def render(instance)
+ instance.to_msgpack
+ end
+
+ def render_multiple(instances)
+ instances.to_msgpack
+ end
+
+ def supported?(klass)
+ Puppet.features.msgpack? && klass.method_defined?(:to_msgpack)
+ end
+end
+
Puppet::Network::FormatHandler.create_serialized_formats(:yaml) do
def intern(klass, text)
data = YAML.load(text, :safe => true, :deserialize_symbols => true)
diff --git a/lib/puppet/network/http/connection.rb b/lib/puppet/network/http/connection.rb
index c75bab97c..adb166439 100644
--- a/lib/puppet/network/http/connection.rb
+++ b/lib/puppet/network/http/connection.rb
@@ -24,17 +24,17 @@ module Puppet::Network::HTTP
OPTION_DEFAULTS = {
:use_ssl => true,
- :verify_peer => true,
+ :verify => nil,
:redirect_limit => 10
}
- # Creates a new HTTP client connection to `host`:`port`.
+ # Creates a new HTTP client connection to `host`:`port`.
# @param host [String] the host to which this client will connect to
# @param port [Fixnum] the port to which this client will connect to
# @param options [Hash] options influencing the properties of the created connection,
# the following options are recognized:
# :use_ssl [Boolean] true to connect with SSL, false otherwise, defaults to true
- # :verify_peer [Boolean] true to verify the peer's certificate, false otherwise, defaults to true
+ # :verify [#setup_connection] An object that will configure any verification to do on the connection
# :redirect_limit [Fixnum] the number of allowed redirections, defaults to 10
# passing any other option in the options hash results in a Puppet::Error exception
# @note the HTTP connection itself happens lazily only when {#request}, or one of the {#get}, {#post}, {#delete}, {#head} or {#put} is called
@@ -48,7 +48,7 @@ module Puppet::Network::HTTP
options = OPTION_DEFAULTS.merge(options)
@use_ssl = options[:use_ssl]
- @verify_peer = options[:verify_peer]
+ @verify = options[:verify]
@redirect_limit = options[:redirect_limit]
end
@@ -128,23 +128,19 @@ module Puppet::Network::HTTP
end
def execute_request(method, *args)
- ssl_validator = Puppet::SSL::Validator.new(:ssl_configuration => ssl_configuration)
- # Perform our own validation of the SSL connection in addition to OpenSSL
- ssl_validator.register_verify_callback(connection)
-
response = connection.send(method, *args)
# Check the peer certs and warn if they're nearing expiration.
- warn_if_near_expiration(*ssl_validator.peer_certs)
+ warn_if_near_expiration(*@verify.peer_certs)
response
rescue OpenSSL::SSL::SSLError => error
if error.message.include? "certificate verify failed"
msg = error.message
- msg << ": [" + ssl_validator.verify_errors.join('; ') + "]"
+ msg << ": [" + @verify.verify_errors.join('; ') + "]"
raise Puppet::Error, msg
elsif error.message =~ /hostname (\w+ )?not match/
- leaf_ssl_cert = ssl_validator.peer_certs.last
+ leaf_ssl_cert = @verify.peer_certs.last
valid_certnames = [leaf_ssl_cert.name, *leaf_ssl_cert.subject_alt_names].uniq
msg = valid_certnames.length > 1 ? "one of #{valid_certnames.join(', ')}" : valid_certnames.first
@@ -181,23 +177,7 @@ module Puppet::Network::HTTP
# Use cert information from a Puppet client to set up the http object.
def cert_setup
- if @verify_peer and FileTest.exist?(Puppet[:hostcert]) and FileTest.exist?(ssl_configuration.ca_auth_file)
- @connection.cert_store = ssl_host.ssl_store
- @connection.ca_file = ssl_configuration.ca_auth_file
- @connection.cert = ssl_host.certificate.content
- @connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
- @connection.key = ssl_host.key.content
- else
- # We don't have the local certificates, so we don't do any verification
- # or setup at this early stage. REVISIT: Shouldn't we supply the local
- # certificate details if we have them? The original code didn't.
- # --daniel 2012-06-03
-
- # Ruby 1.8 defaulted to this, but 1.9 defaults to peer verify,
- # and we almost always talk to a dedicated, not-standard CA that
- # isn't trusted out of the box. This forces the expected state.
- @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
- end
+ @verify.setup_connection(@connection)
end
# This method largely exists for testing purposes, so that we can
@@ -205,18 +185,5 @@ module Puppet::Network::HTTP
def create_connection(*args)
Net::HTTP.new(*args)
end
-
- # Use the global localhost instance.
- def ssl_host
- Puppet::SSL::Host.localhost
- end
-
- def ssl_configuration
- @ssl_configuration ||= Puppet::SSL::Configuration.new(
- Puppet[:localcacert],
- :ca_chain_file => Puppet[:ssl_client_ca_chain],
- :ca_auth_file => Puppet[:ssl_client_ca_auth]
- )
- end
end
end
diff --git a/lib/puppet/network/http/handler.rb b/lib/puppet/network/http/handler.rb
index b8abd80e4..3b86195d0 100644
--- a/lib/puppet/network/http/handler.rb
+++ b/lib/puppet/network/http/handler.rb
@@ -89,15 +89,20 @@ module Puppet::Network::HTTP::Handler
configure_profiler(request_headers, request_params)
Puppet::Util::Profiler.profile("Processed request #{request_method} #{request_path}") do
- indirection, method, key, params = uri2indirection(request_method, request_path, request_params)
+ indirection_name, method, key, params = uri2indirection(request_method, request_path, request_params)
- check_authorization(indirection, method, key, params)
+ check_authorization(indirection_name, method, key, params)
warn_if_near_expiration(client_cert(request))
+ indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym)
+ raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection
+
+ if !indirection.allow_remote_requests?
+ raise HTTPNotFoundError, "No handler for #{indirection.name}"
+ end
+
send("do_#{method}", indirection, key, params, request, response)
end
- rescue SystemExit,NoMemoryError
- raise
rescue HTTPError => e
return do_http_control_exception(response, e)
rescue Exception => e
@@ -129,19 +134,13 @@ module Puppet::Network::HTTP::Handler
set_response(response, exception.to_s, status)
end
- def model(indirection_name)
- raise ArgumentError, "Could not find indirection '#{indirection_name}'" unless indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym)
- indirection.model
- end
-
# Execute our find.
- def do_find(indirection_name, key, params, request, response)
- model_class = model(indirection_name)
- unless result = model_class.indirection.find(key, params)
- raise HTTPNotFoundError, "Could not find #{indirection_name} #{key}"
+ def do_find(indirection, key, params, request, response)
+ unless result = indirection.find(key, params)
+ raise HTTPNotFoundError, "Could not find #{indirection.name} #{key}"
end
- format = accepted_response_formatter_for(model_class, request)
+ format = accepted_response_formatter_for(indirection.model, request)
set_content_type(response, format)
rendered_result = result
@@ -157,9 +156,9 @@ module Puppet::Network::HTTP::Handler
end
# Execute our head.
- def do_head(indirection_name, key, params, request, response)
- unless self.model(indirection_name).indirection.head(key, params)
- raise HTTPNotFoundError, "Could not find #{indirection_name} #{key}"
+ def do_head(indirection, key, params, request, response)
+ unless indirection.head(key, params)
+ raise HTTPNotFoundError, "Could not find #{indirection.name} #{key}"
end
# No need to set a response because no response is expected from a
@@ -167,38 +166,35 @@ module Puppet::Network::HTTP::Handler
end
# Execute our search.
- def do_search(indirection_name, key, params, request, response)
- model = self.model(indirection_name)
- result = model.indirection.search(key, params)
+ def do_search(indirection, key, params, request, response)
+ result = indirection.search(key, params)
if result.nil?
- raise HTTPNotFoundError, "Could not find instances in #{indirection_name} with '#{key}'"
+ raise HTTPNotFoundError, "Could not find instances in #{indirection.name} with '#{key}'"
end
- format = accepted_response_formatter_for(model, request)
+ format = accepted_response_formatter_for(indirection.model, request)
set_content_type(response, format)
- set_response(response, model.render_multiple(format, result))
+ set_response(response, indirection.model.render_multiple(format, result))
end
# Execute our destroy.
- def do_destroy(indirection_name, key, params, request, response)
- model_class = model(indirection_name)
- formatter = accepted_response_formatter_or_yaml_for(model_class, request)
+ def do_destroy(indirection, key, params, request, response)
+ formatter = accepted_response_formatter_or_yaml_for(indirection.model, request)
- result = model_class.indirection.destroy(key, params)
+ result = indirection.destroy(key, params)
set_content_type(response, formatter)
set_response(response, formatter.render(result))
end
# Execute our save.
- def do_save(indirection_name, key, params, request, response)
- model_class = model(indirection_name)
- formatter = accepted_response_formatter_or_yaml_for(model_class, request)
- sent_object = read_body_into_model(model_class, request)
+ def do_save(indirection, key, params, request, response)
+ formatter = accepted_response_formatter_or_yaml_for(indirection.model, request)
+ sent_object = read_body_into_model(indirection.model, request)
- result = model_class.indirection.save(sent_object, key)
+ result = indirection.save(sent_object, key)
set_content_type(response, formatter)
set_response(response, formatter.render(result))
diff --git a/lib/puppet/network/http/webrick.rb b/lib/puppet/network/http/webrick.rb
index 123b493c0..869900dad 100644
--- a/lib/puppet/network/http/webrick.rb
+++ b/lib/puppet/network/http/webrick.rb
@@ -10,7 +10,6 @@ require 'puppet/ssl/configuration'
class Puppet::Network::HTTP::WEBrick
def initialize
@listening = false
- @mutex = Mutex.new
end
def listen(address, port)
@@ -25,34 +24,28 @@ class Puppet::Network::HTTP::WEBrick
@server.mount('/', Puppet::Network::HTTP::WEBrickREST, :this_value_is_apparently_necessary_but_unused)
- @mutex.synchronize do
- raise "WEBrick server is already listening" if @listening
- @listening = true
- @thread = Thread.new {
- @server.start { |sock|
- raise "Client disconnected before connection could be established" unless IO.select([sock],nil,nil,6.2)
- sock.accept
- @server.run(sock)
- }
- }
- sleep 0.1 until @server.status == :Running
+ raise "WEBrick server is already listening" if @listening
+ @listening = true
+ @thread = Thread.new do
+ @server.start do |sock|
+ raise "Client disconnected before connection could be established" unless IO.select([sock],nil,nil,6.2)
+ sock.accept
+ @server.run(sock)
+ end
end
+ sleep 0.1 until @server.status == :Running
end
def unlisten
- @mutex.synchronize do
- raise "WEBrick server is not listening" unless @listening
- @server.shutdown
- wait_for_shutdown
- @server = nil
- @listening = false
- end
+ raise "WEBrick server is not listening" unless @listening
+ @server.shutdown
+ wait_for_shutdown
+ @server = nil
+ @listening = false
end
def listening?
- @mutex.synchronize do
- @listening
- end
+ @listening
end
def wait_for_shutdown
diff --git a/lib/puppet/network/http_pool.rb b/lib/puppet/network/http_pool.rb
index f037f318e..97094c9ad 100644
--- a/lib/puppet/network/http_pool.rb
+++ b/lib/puppet/network/http_pool.rb
@@ -2,18 +2,52 @@ require 'puppet/network/http/connection'
module Puppet::Network; end
-# This class is basically a placeholder for managing a pool of HTTP connections;
-# at present it does not actually attempt to pool them. Historically, it did
-# attempt to do so, but this didn't work well based on Puppet's threading model.
-# The pooling functionality has been removed, but this abstraction is still here
-# because the API is used in various places and because it could be useful
-# should we decide to implement pooling at some point in the future.
+# This module contains the factory methods that should be used for getting a
+# Puppet::Network::HTTP::Connection instance.
+#
+# The name "HttpPool" is a misnomer, and a leftover of history, but we would
+# like to make this cache connections in the future.
+#
+# @api public
+#
module Puppet::Network::HttpPool
- # Retrieve a cached http instance if caching is enabled, else return
- # a new one.
+ # Retrieve a connection for the given host and port.
+ #
+ # @param host [String] The hostname to connect to
+ # @param port [Integer] The port on the host to connect to
+ # @param use_ssl [Boolean] Whether to use an SSL connection
+ # @param verify_peer [Boolean] Whether to verify the peer credentials, if possible. Verification will not take place if the CA certificate is missing.
+ # @return [Puppet::Network::HTTP::Connection]
+ #
+ # @api public
+ #
def self.http_instance(host, port, use_ssl = true, verify_peer = true)
- Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => use_ssl, :verify_peer => verify_peer)
+ verifier = if verify_peer
+ Puppet::SSL::Validator.default_validator()
+ else
+ Puppet::SSL::Validator.no_validator()
+ end
+
+ Puppet::Network::HTTP::Connection.new(host, port,
+ :use_ssl => use_ssl,
+ :verify => verifier)
end
+ # Get an http connection that will be secured with SSL and have the
+ # connection verified with the given verifier
+ #
+ # @param host [String] the DNS name to connect to
+ # @param port [Integer] the port to connect to
+ # @param verifier [#setup_connection, #peer_certs, #verify_errors] An object that will setup the appropriate
+ # verification on a Net::HTTP instance and report any errors and the certificates used.
+ # @return [Puppet::Network::HTTP::Connection]
+ #
+ # @api public
+ #
+ def self.http_ssl_instance(host, port, verifier = Puppet::SSL::Validator.default_validator())
+ Puppet::Network::HTTP::Connection.new(host, port,
+ :use_ssl => true,
+ :verify => verifier)
+ end
end
diff --git a/lib/puppet/network/rights.rb b/lib/puppet/network/rights.rb
index f7420a90e..f7420a90e 100755..100644
--- a/lib/puppet/network/rights.rb
+++ b/lib/puppet/network/rights.rb
diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb
index b01a4665d..09b307927 100644
--- a/lib/puppet/node.rb
+++ b/lib/puppet/node.rb
@@ -16,7 +16,7 @@ class Puppet::Node
indirects :node, :terminus_setting => :node_terminus, :doc => "Where to find node information.
A node is composed of its name, its facts, and its environment."
- attr_accessor :name, :classes, :source, :ipaddress, :parameters
+ attr_accessor :name, :classes, :source, :ipaddress, :parameters, :trusted_data
attr_reader :time, :facts
::PSON.register_document_type('Node',self)
@@ -31,17 +31,25 @@ class Puppet::Node
node
end
- def to_pson(*args)
+ def to_data_hash
result = {
+ 'name' => name,
+ 'environment' => environment.name,
+ }
+ result['classes'] = classes unless classes.empty?
+ result['parameters'] = parameters unless parameters.empty?
+ result
+ end
+
+ def to_pson_data_hash(*args)
+ {
'document_type' => "Node",
- 'data' => {}
+ 'data' => to_data_hash,
}
- result['data']['name'] = name
- result['data']['classes'] = classes unless classes.empty?
- result['data']['parameters'] = parameters unless parameters.empty?
- result['data']['environment'] = environment.name
+ end
- result.to_pson(*args)
+ def to_pson(*args)
+ to_pson_data_hash.to_pson(*args)
end
def environment
@@ -84,6 +92,7 @@ class Puppet::Node
# Merge the node facts with parameters from the node source.
def fact_merge
if @facts = Puppet::Node::Facts.indirection.find(name, :environment => environment)
+ @facts.sanitize
merge(@facts.values)
end
rescue => detail
@@ -143,4 +152,11 @@ class Puppet::Node
end
tmp.reverse
end
+
+ # Ensures the data is frozen
+ #
+ def trusted_data=(data)
+ Puppet.warning("Trusted node data modified for node #{name}") unless @trusted_data.nil?
+ @trusted_data = data.freeze
+ end
end
diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb
index 25b454638..f79b8b35b 100644
--- a/lib/puppet/node/environment.rb
+++ b/lib/puppet/node/environment.rb
@@ -10,20 +10,18 @@ end
# Puppet::Node::Environment acts as a container for all configuration
# that is expected to vary between environments.
#
-# ## Thread local variables
+# ## Global variables
#
-# The Puppet::Node::Environment uses a number of `Thread.current` variables.
-# Since all web servers that Puppet runs on are single threaded these
-# variables are effectively global.
+# The Puppet::Node::Environment uses a number of global variables.
#
-# ### `Thread.current[:environment]`
+# ### `$environment`
#
-# The 'environment' thread variable represents the current environment that's
+# The 'environment' global variable represents the current environment that's
# being used in the compiler.
#
-# ### `Thread.current[:known_resource_types]`
+# ### `$known_resource_types`
#
-# The 'known_resource_types' thread variable represents a singleton instance
+# The 'known_resource_types' global variable represents a singleton instance
# of the Puppet::Resource::TypeCollection class. The variable is discarded
# and regenerated if it is accessed by an environment that doesn't match the
# environment of the 'known_resource_types'
@@ -129,7 +127,7 @@ class Puppet::Node::Environment
# @return [Puppet::Node::Environment] the currently set environment if one
# has been explicitly set, else it will return the '*root*' environment
def self.current
- Thread.current[:environment] || root
+ $environment || root
end
# Set the environment for the current thread
@@ -145,7 +143,7 @@ class Puppet::Node::Environment
#
# @param env [Puppet::Node::Environment]
def self.current=(env)
- Thread.current[:environment] = new(env)
+ $environment = new(env)
end
@@ -164,7 +162,7 @@ class Puppet::Node::Environment
# @api private
def self.clear
@seen.clear
- Thread.current[:environment] = nil
+ $environment = nil
end
# @!attribute [r] name
@@ -192,17 +190,16 @@ class Puppet::Node::Environment
# @param name [Symbol] The environment name
def initialize(name)
@name = name
- extend MonitorMixin
end
# The current global TypeCollection
#
# @note The environment is loosely coupled with the {Puppet::Resource::TypeCollection}
# class. While there is a 1:1 relationship between an environment and a
- # TypeCollection instance, there is only one TypeCollection instance available
- # at any given time. It is stored in the Thread.current collection as
- # 'known_resource_types'. 'known_resource_types' is accessed as an instance
- # method, but is global to all environment variables.
+ # TypeCollection instance, there is only one TypeCollection instance
+ # available at any given time. It is stored in `$known_resource_types`.
+ # `$known_resource_types` is accessed as an instance method, but is global
+ # to all environment variables.
#
# @api public
# @return [Puppet::Resource::TypeCollection] The current global TypeCollection
@@ -212,14 +209,15 @@ class Puppet::Node::Environment
# 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 {
+ $known_resource_types = nil if $known_resource_types && $known_resource_types.environment != self
+ $known_resource_types ||=
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, '')
+ @known_resource_types
+ else
+ @known_resource_types
end
- @known_resource_types
- }
end
# Yields each modules' plugin directory if the plugin directory (modulename/lib)
diff --git a/lib/puppet/node/facts.rb b/lib/puppet/node/facts.rb
index b18a1e88d..2be4e68b9 100755..100644
--- a/lib/puppet/node/facts.rb
+++ b/lib/puppet/node/facts.rb
@@ -84,16 +84,33 @@ class Puppet::Node::Facts
new_facts
end
- def to_pson(*args)
+ def to_data_hash
result = {
'name' => name,
'values' => strip_internal,
}
- result['timestamp'] = timestamp if timestamp
- result['expiration'] = expiration if expiration
+ if timestamp
+ if timestamp.is_a? Time
+ result['timestamp'] = timestamp.iso8601(9)
+ else
+ result['timestamp'] = timestamp
+ end
+ end
+
+ if expiration
+ if expiration.is_a? Time
+ result['expiration'] = expiration.iso8601(9)
+ else
+ result['expiration'] = expiration
+ end
+ end
- result.to_pson(*args)
+ result
+ end
+
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
# Add internal data to the facts for storage.
@@ -109,8 +126,6 @@ class Puppet::Node::Facts
self.values['_timestamp']
end
- private
-
# Strip out that internal data.
def strip_internal
newvals = values.dup
@@ -118,6 +133,8 @@ class Puppet::Node::Facts
newvals
end
+ private
+
def sanitize_fact(fact)
if fact.is_a? Hash then
ret = {}
diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb
index cce9fb57e..f265a99ae 100644
--- a/lib/puppet/parameter.rb
+++ b/lib/puppet/parameter.rb
@@ -116,10 +116,13 @@ class Puppet::Parameter
@doc ||= ""
unless defined?(@addeddocvals)
- @doc += value_collection.doc
+ @doc = Puppet::Util::Docs.scrub(@doc)
+ if vals = value_collection.doc
+ @doc << "\n\n#{vals}"
+ end
if f = self.required_features
- @doc += " Requires features #{f.flatten.collect { |f| f.to_s }.join(" ")}."
+ @doc << "\n\nRequires features #{f.flatten.collect { |f| f.to_s }.join(" ")}."
end
@addeddocvals = true
end
@@ -567,6 +570,16 @@ class Puppet::Parameter
"'#{value}'"
end
end
+
+ # @comment Document post_compile_hook here as it does not exist anywhere (called from type if implemented)
+ # @!method post_compile()
+ # @since 3.4.0
+ # @api public
+ # @abstract A subclass may implement this - it is not implemented in the Parameter class
+ # This method may be implemented by a parameter in order to perform actions during compilation
+ # after all resources have been added to the catalog.
+ # @see Puppet::Type#finish
+ # @see Puppet::Parser::Compiler#finish
end
require 'puppet/parameter/path'
diff --git a/lib/puppet/parameter/boolean.rb b/lib/puppet/parameter/boolean.rb
index 86f0c05dd..11ad80729 100644
--- a/lib/puppet/parameter/boolean.rb
+++ b/lib/puppet/parameter/boolean.rb
@@ -7,4 +7,9 @@ class Puppet::Parameter::Boolean < Puppet::Parameter
def unsafe_munge(value)
Puppet::Coercion.boolean(value)
end
+
+ def self.initvars
+ super
+ @value_collection.newvalues(*Puppet::Coercion.boolean_values)
+ end
end
diff --git a/lib/puppet/parameter/value_collection.rb b/lib/puppet/parameter/value_collection.rb
index 7e2bee331..4cbd95d6a 100644
--- a/lib/puppet/parameter/value_collection.rb
+++ b/lib/puppet/parameter/value_collection.rb
@@ -33,17 +33,19 @@ class Puppet::Parameter::ValueCollection
unless defined?(@doc)
@doc = ""
unless values.empty?
- @doc += " Valid values are "
- @doc += @strings.collect do |value|
+ @doc << "Valid values are "
+ @doc << @strings.collect do |value|
if aliases = value.aliases and ! aliases.empty?
"`#{value.name}` (also called `#{aliases.join(", ")}`)"
else
"`#{value.name}`"
end
- end.join(", ") + "."
+ end.join(", ") << ". "
end
- @doc += " Values can match `" + regexes.join("`, `") + "`." unless regexes.empty?
+ unless regexes.empty?
+ @doc << "Values can match `#{regexes.join("`, `")}`."
+ end
end
@doc
diff --git a/lib/puppet/parser/ast/resourceparam.rb b/lib/puppet/parser/ast/resourceparam.rb
index a3be0a674..04e945c52 100644
--- a/lib/puppet/parser/ast/resourceparam.rb
+++ b/lib/puppet/parser/ast/resourceparam.rb
@@ -11,9 +11,10 @@ class Puppet::Parser::AST
# Return the parameter and the value.
def evaluate(scope)
+ value = @value.safeevaluate(scope)
return Puppet::Parser::Resource::Param.new(
:name => @param,
- :value => @value.safeevaluate(scope),
+ :value => value.nil? ? :undef : value,
:source => scope.source, :line => self.line, :file => self.file,
:add => self.add
)
diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
index 20f668504..2ea34c1de 100644
--- a/lib/puppet/parser/compiler.rb
+++ b/lib/puppet/parser/compiler.rb
@@ -17,13 +17,8 @@ class Puppet::Parser::Compiler
include Puppet::Resource::TypeCollectionHelper
def self.compile(node)
- # We get these from the environment and only cache them in a thread
- # variable for the duration of the compilation. If nothing else is using
- # the thread, though, we can leave 'em hanging round with no ill effects,
- # and this is safer than cleaning them at the end and assuming that will
- # stick until the next entry to this function.
- Thread.current[:known_resource_types] = nil
- Thread.current[:env_module_directories] = nil
+ $known_resource_types = nil
+ $env_module_directories = nil
# ...and we actually do the compile now we have caching ready.
new(node).compile.to_resource
@@ -143,8 +138,27 @@ class Puppet::Parser::Compiler
end
# Evaluate all of the classes specified by the node.
+ # Classes with parameters are evaluated as if they were declared.
+ # Classes without parameters or with an empty set of parameters are evaluated
+ # as if they were included. This means classes with an empty set of
+ # parameters won't conflict even if the class has already been included.
def evaluate_node_classes
- evaluate_classes(@node.classes, @node_scope || topscope)
+ if @node.classes.is_a? Hash
+ classes_with_params, classes_without_params = @node.classes.partition {|name,params| params and !params.empty?}
+
+ # The results from Hash#partition are arrays of pairs rather than hashes,
+ # so we have to convert to the forms evaluate_classes expects (Hash, and
+ # Array of class names)
+ classes_with_params = Hash[classes_with_params]
+ classes_without_params.map!(&:first)
+ else
+ classes_with_params = {}
+ classes_without_params = @node.classes
+ end
+
+ evaluate_classes(classes_without_params, @node_scope || topscope)
+
+ evaluate_classes(classes_with_params, @node_scope || topscope)
end
# Evaluate each specified class in turn. If there are any classes we can't
@@ -487,10 +501,12 @@ class Puppet::Parser::Compiler
node.parameters.each do |param, value|
@topscope[param.to_s] = value
end
-
# These might be nil.
catalog.client_version = node.parameters["clientversion"]
catalog.server_version = node.parameters["serverversion"]
+ if Puppet[:trusted_node_data]
+ @topscope.set_trusted(node.trusted_data)
+ end
end
def create_settings_scope
diff --git a/lib/puppet/parser/files.rb b/lib/puppet/parser/files.rb
index 1d5b64966..49f36019f 100644
--- a/lib/puppet/parser/files.rb
+++ b/lib/puppet/parser/files.rb
@@ -41,7 +41,7 @@ module Puppet; module Parser; module Files
template_paths.collect { |path|
File::join(path, template)
}.each do |f|
- return f if FileTest.exist?(f)
+ return f if Puppet::FileSystem::File.exist?(f)
end
end
diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index 7015ec4d9..360387727 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -1,6 +1,5 @@
require 'puppet/util/autoload'
require 'puppet/parser/scope'
-require 'monitor'
# A module for managing parser functions. Each specified function
# is added to a central module that then gets included into the Scope
@@ -18,8 +17,8 @@ module Puppet::Parser::Functions
#
# @api private
def self.reset
- @functions = Hash.new { |h,k| h[k] = {} }.extend(MonitorMixin)
- @modules = Hash.new.extend(MonitorMixin)
+ @functions = Hash.new { |h,k| h[k] = {} }
+ @modules = Hash.new
# Runs a newfunction to create a function for each of the log levels
Puppet::Util::Log.levels.each do |level|
@@ -46,9 +45,7 @@ module Puppet::Parser::Functions
if env and ! env.is_a?(Puppet::Node::Environment)
env = Puppet::Node::Environment.new(env)
end
- @modules.synchronize {
- @modules[ (env || Environment.current || Environment.root).name ] ||= Module.new
- }
+ @modules[ (env || Environment.current || Environment.root).name ] ||= Module.new
end
# Create a new Puppet DSL function.
@@ -170,11 +167,9 @@ module Puppet::Parser::Functions
name = name.intern
func = nil
- @functions.synchronize do
- unless func = get_function(name)
- autoloader.load(name, Environment.current)
- func = get_function(name)
- end
+ unless func = get_function(name)
+ autoloader.load(name, Environment.current)
+ func = get_function(name)
end
if func
@@ -190,14 +185,14 @@ module Puppet::Parser::Functions
ret = ""
merged_functions.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, hash|
- ret += "#{name}\n#{"-" * name.to_s.length}\n"
+ ret << "#{name}\n#{"-" * name.to_s.length}\n"
if hash[:doc]
- ret += Puppet::Util::Docs.scrub(hash[:doc])
+ ret << Puppet::Util::Docs.scrub(hash[:doc])
else
- ret += "Undocumented.\n"
+ ret << "Undocumented.\n"
end
- ret += "\n\n- *Type*: #{hash[:type]}\n\n"
+ ret << "\n\n- *Type*: #{hash[:type]}\n\n"
end
ret
@@ -229,9 +224,7 @@ module Puppet::Parser::Functions
private
def merged_functions
- @functions.synchronize {
- @functions[Environment.root].merge(@functions[Environment.current])
- }
+ @functions[Environment.root].merge(@functions[Environment.current])
end
def get_function(name)
@@ -241,9 +234,7 @@ module Puppet::Parser::Functions
def add_function(name, func)
name = name.intern
- @functions.synchronize {
- @functions[Environment.current][name] = func
- }
+ @functions[Environment.current][name] = func
end
end
diff --git a/lib/puppet/parser/functions/collect.rb b/lib/puppet/parser/functions/collect.rb
index 5485b36af..e30a80bb1 100644
--- a/lib/puppet/parser/functions/collect.rb
+++ b/lib/puppet/parser/functions/collect.rb
@@ -1,44 +1,15 @@
-require 'puppet/parser/ast/lambda'
-
Puppet::Parser::Functions::newfunction(
:collect,
:type => :rvalue,
:arity => 2,
:doc => <<-'ENDHEREDOC') do |args|
- Applies a parameterized block to each element in a sequence of entries from the first
- argument and returns an array with the result of each invocation of the parameterized block.
-
- This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
- a parameterized block as produced by the puppet syntax:
-
- $a.collect |$x| { ... }
-
- When the first argument `$a` is an Array, the block is called with each entry in turn. When the first argument
- is a hash the entry is an array with `[key, value]`.
-
- *Examples*
+ The 'collect' function has been renamed to 'map'. Please update your manifests.
- # Turns hash into array of values
- $a.collect |$x|{ $x[1] }
-
- # Turns hash into array of keys
- $a.collect |$x| { $x[0] }
-
- - Since 3.2
+ The collect function is reserved for future use.
+ - Removed as of 3.4
- requires `parser = future`.
ENDHEREDOC
- receiver = args[0]
- pblock = args[1]
-
- raise ArgumentError, ("collect(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
-
- case receiver
- when Array
- when Hash
- else
- raise ArgumentError, ("collect(): wrong argument type (#{receiver.class}; must be an Array or a Hash.")
- end
-
- receiver.to_a.collect {|x| pblock.call(self, x) }
-end
+ raise NotImplementedError,
+ "The 'collect' function has been renamed to 'map'. Please update your manifests."
+end \ No newline at end of file
diff --git a/lib/puppet/parser/functions/contain.rb b/lib/puppet/parser/functions/contain.rb
new file mode 100644
index 000000000..8eb514561
--- /dev/null
+++ b/lib/puppet/parser/functions/contain.rb
@@ -0,0 +1,26 @@
+# Called within a class definition, establishes a containment
+# relationship with another class
+
+Puppet::Parser::Functions::newfunction(
+ :contain,
+ :arity => -2,
+ :doc => "Contain one or more classes inside the current class. If any of
+these classes are undeclared, they will be declared as if called with the
+`include` function. Accepts a class name, an array of class names, or a
+comma-separated list of class names.
+
+A contained class will not be applied before the containing class is
+begun, and will be finished before the containing class is finished.
+"
+) do |classes|
+ scope = self
+
+ scope.function_include(classes)
+
+ classes.each do |class_name|
+ class_resource = scope.catalog.resource("Class", class_name)
+ if ! scope.catalog.edge?(scope.resource, class_resource)
+ scope.catalog.add_edge(scope.resource, class_resource)
+ end
+ end
+end
diff --git a/lib/puppet/parser/functions/create_resources.rb b/lib/puppet/parser/functions/create_resources.rb
index 733dc4511..1c3b910b0 100644
--- a/lib/puppet/parser/functions/create_resources.rb
+++ b/lib/puppet/parser/functions/create_resources.rb
@@ -44,6 +44,11 @@ Puppet::Parser::Functions::newfunction(:create_resources, :arity => -3, :doc =>
ENDHEREDOC
raise ArgumentError, ("create_resources(): wrong number of arguments (#{args.length}; must be 2 or 3)") if args.length > 3
+ raise ArgumentError, ('create_resources(): second argument must be a hash') unless args[1].is_a?(Hash)
+ if args.length == 3
+ raise ArgumentError, ('create_resources(): third argument, if provided, must be a hash') unless args[2].is_a?(Hash)
+ end
+
type, instances, defaults = args
defaults ||= {}
diff --git a/lib/puppet/parser/functions/extlookup.rb b/lib/puppet/parser/functions/extlookup.rb
index 2a81ccc4e..293a9ea62 100644
--- a/lib/puppet/parser/functions/extlookup.rb
+++ b/lib/puppet/parser/functions/extlookup.rb
@@ -100,7 +100,7 @@ This is for back compatibility to interpolate variables with %. % interpolation
# if we got a custom data file, put it first in the array of search files
if datafile != ""
- datafiles << extlookup_datadir + "/#{datafile}.csv" if File.exists?(extlookup_datadir + "/#{datafile}.csv")
+ datafiles << extlookup_datadir + "/#{datafile}.csv" if Puppet::FileSystem::File.exist?(extlookup_datadir + "/#{datafile}.csv")
end
extlookup_precedence.each do |d|
@@ -111,7 +111,7 @@ This is for back compatibility to interpolate variables with %. % interpolation
datafiles.each do |file|
if desired.nil?
- if File.exists?(file)
+ if Puppet::FileSystem::File.exist?(file)
result = CSV.read(file).find_all do |r|
r[0] == key
end
diff --git a/lib/puppet/parser/functions/file.rb b/lib/puppet/parser/functions/file.rb
index 569266a3b..89d78f8ba 100644
--- a/lib/puppet/parser/functions/file.rb
+++ b/lib/puppet/parser/functions/file.rb
@@ -10,7 +10,7 @@ Puppet::Parser::Functions::newfunction(
unless Puppet::Util.absolute_path?(file)
raise Puppet::ParseError, "Files must be fully qualified"
end
- if FileTest.exists?(file)
+ if Puppet::FileSystem::File.exist?(file)
ret = File.read(file)
break
end
diff --git a/lib/puppet/parser/functions/reject.rb b/lib/puppet/parser/functions/filter.rb
index d19471102..7894fa48f 100644
--- a/lib/puppet/parser/functions/reject.rb
+++ b/lib/puppet/parser/functions/filter.rb
@@ -1,47 +1,48 @@
require 'puppet/parser/ast/lambda'
Puppet::Parser::Functions::newfunction(
-:reject,
+:filter,
:type => :rvalue,
:arity => 2,
:doc => <<-'ENDHEREDOC') do |args|
Applies a parameterized block to each element in a sequence of entries from the first
- argument and returns an array with the entires for which the block did *not* evaluate to true.
+ argument and returns an array or hash (same type as left operand)
+ with the entries for which the block evaluates to true.
This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
a parameterized block as produced by the puppet syntax:
- $a.reject |$x| { ... }
+ $a.filter |$x| { ... }
When the first argument is an Array, the block is called with each entry in turn. When the first argument
- is a hash the entry is an array with `[key, value]`.
+ is a Hash the entry is an array with `[key, value]`.
The returned filtered object is of the same type as the receiver.
*Examples*
- # selects all that does not end with berry
- $a = ["rasberry", "blueberry", "orange"]
- $a.reject |$x| { $x =~ /berry$/ }
+ # selects all that end with berry
+ $a = ["raspberry", "blueberry", "orange"]
+ $a.filter |$x| { $x =~ /berry$/ }
- - Since 3.2
+ - Since 3.4
- requires `parser = future`.
ENDHEREDOC
receiver = args[0]
pblock = args[1]
- raise ArgumentError, ("reject(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
+ raise ArgumentError, ("filter(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
case receiver
when Array
- receiver.reject {|x| pblock.call(self, x) }
+ receiver.select {|x| pblock.call(self, x) }
when Hash
- result = receiver.reject {|x, y| pblock.call(self, [x, y]) }
+ result = receiver.select {|x, y| pblock.call(self, [x, y]) }
# Ruby 1.8.7 returns Array
result = Hash[result] unless result.is_a? Hash
result
else
- raise ArgumentError, ("reject(): wrong argument type (#{receiver.class}; must be an Array or a Hash.")
+ raise ArgumentError, ("filter(): wrong argument type (#{receiver.class}; must be an Array or a Hash.")
end
end
diff --git a/lib/puppet/parser/functions/foreach.rb b/lib/puppet/parser/functions/foreach.rb
deleted file mode 100644
index 113e96a58..000000000
--- a/lib/puppet/parser/functions/foreach.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-Puppet::Parser::Functions::newfunction(
-:foreach,
-:type => :rvalue,
-:arity => 2,
-:doc => <<-'ENDHEREDOC') do |args|
- Applies a parameterized block to each element in a sequence of selected entries from the first
- argument and returns the first argument.
-
- This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
- a parameterized block as produced by the puppet syntax:
-
- $a.foreach {|$x| ... }
-
- When the first argument is an Array, the parameterized block should define one or two block parameters.
- For each application of the block, the next element from the array is selected, and it is passed to
- the block if the block has one parameter. If the block has two parameters, the first is the elements
- index, and the second the value. The index starts from 0.
-
- $a.foreach {|$index, $value| ... }
-
- When the first argument is a Hash, the parameterized block should define one or two parameters.
- When one parameter is defined, the iteration is performed with each entry as an array of `[key, value]`,
- and when two parameters are defined the iteration is performed with key and value.
-
- $a.foreach {|$entry| ..."key ${$entry[0]}, value ${$entry[1]}" }
- $a.foreach {|$key, $value| ..."key ${key}, value ${value}" }
-
- - Since 3.2
- - requires `parser = future`.
- ENDHEREDOC
- require 'puppet/parser/ast/lambda'
-
- def foreach_Array(o, scope, pblock)
- return nil unless pblock
-
- serving_size = pblock.parameter_count
- if serving_size == 0
- raise ArgumentError, "Block must define at least one parameter; value."
- end
- if serving_size > 2
- raise ArgumentError, "Block must define at most two parameters; index, value"
- end
- enumerator = o.each
- index = 0
- if serving_size == 1
- (o.size).times do
- pblock.call(scope, enumerator.next)
- end
- else
- (o.size).times do
- pblock.call(scope, index, enumerator.next)
- index = index +1
- end
- end
- o
- end
-
- def foreach_Hash(o, scope, pblock)
- return nil unless pblock
- serving_size = pblock.parameter_count
- case serving_size
- when 0
- raise ArgumentError, "Block must define at least one parameter (for hash entry key)."
- when 1
- when 2
- else
- raise ArgumentError, "Block must define at most two parameters (for hash entry key and value)."
- end
- enumerator = o.each_pair
- if serving_size == 1
- (o.size).times do
- pblock.call(scope, enumerator.next)
- end
- else
- (o.size).times do
- pblock.call(scope, *enumerator.next)
- end
- end
- o
- end
-
- raise ArgumentError, ("foreach(): wrong number of arguments (#{args.length}; must be 2)") if args.length != 2
- receiver = args[0]
- pblock = args[1]
- raise ArgumentError, ("foreach(): wrong argument type (#{args[1].class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
-
- case receiver
- when Array
- foreach_Array(receiver, self, pblock)
- when Hash
- foreach_Hash(receiver, self, pblock)
- else
- raise ArgumentError, ("foreach(): wrong argument type (#{args[0].class}; must be an Array or a Hash.")
- end
-end
diff --git a/lib/puppet/parser/functions/fqdn_rand.rb b/lib/puppet/parser/functions/fqdn_rand.rb
index f9bd2d23e..ffdb64e20 100644
--- a/lib/puppet/parser/functions/fqdn_rand.rb
+++ b/lib/puppet/parser/functions/fqdn_rand.rb
@@ -1,12 +1,20 @@
require 'digest/md5'
Puppet::Parser::Functions::newfunction(:fqdn_rand, :arity => -2, :type => :rvalue, :doc =>
- "Generates random numbers based on the node's fqdn. Generated random values
- will be a range from 0 up to and excluding n, where n is the first parameter.
- The second argument specifies a number to add to the seed and is optional, for example:
+ "Usage: `fqdn_rand(MAX, [SEED])`. MAX is required and must be a positive
+ integer; SEED is optional and may be any number or string.
- $random_number = fqdn_rand(30)
- $random_number_seed = fqdn_rand(30,30)") do |args|
+ Generates a random whole number greater than or equal to 0 and less than MAX,
+ combining the `$fqdn` fact and the value of SEED for repeatable randomness.
+ (That is, each node will get a different random number from this function, but
+ a given node's result will be the same every time unless its hostname changes.)
+
+ This function is usually used for spacing out runs of resource-intensive cron
+ tasks that run on many nodes, which could cause a thundering herd or degrade
+ other services if they all fire at once. Adding a SEED can be useful when you
+ have more than one such task and need several unrelated random numbers per
+ node. (For example, `fqdn_rand(30)`, `fqdn_rand(30, 'expensive job 1')`, and
+ `fqdn_rand(30, 'expensive job 2')` will produce totally different numbers.)") do |args|
max = args.shift.to_i
seed = Digest::MD5.hexdigest([self['::fqdn'],args].join(':')).hex
Puppet::Util.deterministic_rand(seed,max)
diff --git a/lib/puppet/parser/functions/include.rb b/lib/puppet/parser/functions/include.rb
index ca8819176..ea7acd936 100644
--- a/lib/puppet/parser/functions/include.rb
+++ b/lib/puppet/parser/functions/include.rb
@@ -1,5 +1,22 @@
# Include the specified classes
-Puppet::Parser::Functions::newfunction(:include, :arity => -2, :doc => "Evaluate one or more classes.") do |vals|
+Puppet::Parser::Functions::newfunction(:include, :arity => -2, :doc => "Declares one or more classes, causing the resources in them to be
+evaluated and added to the catalog. Accepts a class name, an array of class
+names, or a comma-separated list of class names.
+
+The `include` function can be used multiple times on the same class and will
+only declare a given class once. If a class declared with `include` has any
+parameters, Puppet will automatically look up values for them in Hiera, using
+`<class name>::<parameter name>` as the lookup key.
+
+Contrast this behavior with resource-like class declarations
+(`class {'name': parameter => 'value',}`), which must be used in only one place
+per class and can directly set parameters. You should avoid using both `include`
+and resource-like declarations with the same class.
+
+The `include` function does not cause classes to be contained in the class
+where they are declared. For that, see the `contain` function. It also
+does not create a dependency relationship between the declared class and th
+surrounding class; for that, see the `require` function.") do |vals|
if vals.is_a?(Array)
# Protect against array inside array
vals = vals.flatten
diff --git a/lib/puppet/parser/functions/map.rb b/lib/puppet/parser/functions/map.rb
new file mode 100644
index 000000000..3c871bc85
--- /dev/null
+++ b/lib/puppet/parser/functions/map.rb
@@ -0,0 +1,44 @@
+require 'puppet/parser/ast/lambda'
+
+Puppet::Parser::Functions::newfunction(
+:map,
+:type => :rvalue,
+:arity => 2,
+:doc => <<-'ENDHEREDOC') do |args|
+ Applies a parameterized block to each element in a sequence of entries from the first
+ argument and returns an array with the result of each invocation of the parameterized block.
+
+ This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
+ a parameterized block as produced by the puppet syntax:
+
+ $a.map |$x| { ... }
+
+ When the first argument `$a` is an Array, the block is called with each entry in turn. When the first argument
+ is a hash the entry is an array with `[key, value]`.
+
+ *Examples*
+
+ # Turns hash into array of values
+ $a.map |$x|{ $x[1] }
+
+ # Turns hash into array of keys
+ $a.map |$x| { $x[0] }
+
+ - Since 3.4
+ - requires `parser = future`.
+ ENDHEREDOC
+
+ receiver = args[0]
+ pblock = args[1]
+
+ raise ArgumentError, ("map(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
+
+ case receiver
+ when Array
+ when Hash
+ else
+ raise ArgumentError, ("map(): wrong argument type (#{receiver.class}; must be an Array or a Hash.")
+ end
+
+ receiver.to_a.map {|x| pblock.call(self, x) }
+end
diff --git a/lib/puppet/parser/functions/select.rb b/lib/puppet/parser/functions/select.rb
index d5cee8e5c..659f2013c 100644
--- a/lib/puppet/parser/functions/select.rb
+++ b/lib/puppet/parser/functions/select.rb
@@ -1,47 +1,15 @@
-require 'puppet/parser/ast/lambda'
-
Puppet::Parser::Functions::newfunction(
:select,
:type => :rvalue,
:arity => 2,
:doc => <<-'ENDHEREDOC') do |args|
- Applies a parameterized block to each element in a sequence of entries from the first
- argument and returns an array with the entires for which the block evaluates to true.
-
- This function takes two mandatory arguments: the first should be an Array or a Hash, and the second
- a parameterized block as produced by the puppet syntax:
-
- $a.select |$x| { ... }
-
- When the first argument is an Array, the block is called with each entry in turn. When the first argument
- is a hash the entry is an array with `[key, value]`.
-
- The returned filtered object is of the same type as the receiver.
+ The 'select' function has been renamed to 'filter'. Please update your manifests.
- *Examples*
-
- # selects all that end with berry
- $a = ["raspberry", "blueberry", "orange"]
- $a.select |$x| { $x =~ /berry$/ }
-
- - Since 3.2
+ The select function is reserved for future use.
+ - Removed as of 3.4
- requires `parser = future`.
ENDHEREDOC
- receiver = args[0]
- pblock = args[1]
-
- raise ArgumentError, ("select(): wrong argument type (#{pblock.class}; must be a parameterized block.") unless pblock.is_a? Puppet::Parser::AST::Lambda
-
- case receiver
- when Array
- receiver.select {|x| pblock.call(self, x) }
- when Hash
- result = receiver.select {|x, y| pblock.call(self, [x, y]) }
- # Ruby 1.8.7 returns Array
- result = Hash[result] unless result.is_a? Hash
- result
- else
- raise ArgumentError, ("select(): wrong argument type (#{receiver.class}; must be an Array or a Hash.")
- end
-end
+ raise NotImplementedError,
+ "The 'select' function has been renamed to 'filter'. Please update your manifests."
+end \ No newline at end of file
diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb
index fee38d946..65cbef92e 100644
--- a/lib/puppet/parser/lexer.rb
+++ b/lib/puppet/parser/lexer.rb
@@ -347,7 +347,7 @@ class Puppet::Parser::Lexer
def file=(file)
@file = file
@line = 1
- contents = File.exists?(file) ? File.read(file) : ""
+ contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : ""
@scanner = StringScanner.new(contents)
end
diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb
index 2fb231ff4..b67f3c752 100644
--- a/lib/puppet/parser/parser_support.rb
+++ b/lib/puppet/parser/parser_support.rb
@@ -82,7 +82,7 @@ class Puppet::Parser::Parser
def_delegators :@lexer, :file, :string=
def file=(file)
- unless FileTest.exist?(file)
+ unless Puppet::FileSystem::File.exist?(file)
unless file =~ /\.pp$/
file = file + ".pp"
end
diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb
index 6d9f996e7..0b4c6677b 100644
--- a/lib/puppet/parser/resource.rb
+++ b/lib/puppet/parser/resource.rb
@@ -1,12 +1,9 @@
-require 'forwardable'
require 'puppet/resource'
# The primary difference between this class and its
# parent is that this class has rules on who can set
# parameters
class Puppet::Parser::Resource < Puppet::Resource
- extend Forwardable
-
require 'puppet/parser/resource/param'
require 'puppet/util/tagging'
require 'puppet/parser/yaml_trimmer'
@@ -18,7 +15,6 @@ class Puppet::Parser::Resource < Puppet::Resource
include Puppet::Util::MethodHelper
include Puppet::Util::Errors
include Puppet::Util::Logging
- include Puppet::Util::Tagging
include Puppet::Parser::YamlTrimmer
attr_accessor :source, :scope, :collector_id
@@ -56,7 +52,9 @@ class Puppet::Parser::Resource < Puppet::Resource
end
end
- def_delegator :scope, :environment
+ def environment
+ scope.environment
+ end
# Process the stage metaparameter for a class. A containment edge
# is drawn from the class to the stage. The stage for containment
@@ -188,47 +186,10 @@ class Puppet::Parser::Resource < Puppet::Resource
end
end
-
- # Create a Puppet::Resource instance from this parser resource.
- # We plan, at some point, on not needing to do this conversion, but
- # it's sufficient for now.
- def to_resource
- result = Puppet::Resource.new(type, title)
-
- to_hash.each do |p, v|
- if v.is_a?(Puppet::Resource)
- v = Puppet::Resource.new(v.type, v.title)
- elsif v.is_a?(Array)
- # flatten resource references arrays
- v = v.flatten if v.flatten.find { |av| av.is_a?(Puppet::Resource) }
- v = v.collect do |av|
- av = Puppet::Resource.new(av.type, av.title) if av.is_a?(Puppet::Resource)
- av
- end
- end
-
- # If the value is an array with only one value, then
- # convert it to a single value. This is largely so that
- # the database interaction doesn't have to worry about
- # whether it returns an array or a string.
- result[p] = if v.is_a?(Array) and v.length == 1
- v[0]
- else
- v
- end
- end
-
- result.file = self.file
- result.line = self.line
- result.exported = self.exported
- result.virtual = self.virtual
- result.tag(*self.tags)
-
- result
- end
-
# Convert this resource to a RAL resource.
- def_delegator :to_resource, :to_ral
+ def to_ral
+ copy_as_resource.to_ral
+ end
private
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index c249e7566..2cad0c1dd 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -116,6 +116,9 @@ class Puppet::Parser::Scope
compiler.node.name
end
+ # TODO: 19514 - this is smelly; who uses this? functions? templates?
+ # What about trusted facts ? Should untrusted facts be removed from facts?
+ #
def facts
compiler.node.facts
end
@@ -195,8 +198,6 @@ class Puppet::Parser::Scope
extend_with_functions_module
- @tags = []
-
# The symbol table for this scope. This is where we store variables.
@symtable = Ephemeral.new(nil, true)
@@ -461,6 +462,8 @@ class Puppet::Parser::Scope
}
end
+ RESERVED_VARIABLE_NAMES = ['trusted'].freeze
+
# Set a variable in the current scope. This will override settings
# in scopes above, but will not allow variables in the current scope
# to be reassigned.
@@ -474,6 +477,11 @@ class Puppet::Parser::Scope
raise Puppet::ParseError, "Scope variable name #{name.inspect} is a #{name.class}, not a string"
end
+ # Check for reserved variable names
+ if Puppet[:trusted_node_data] && !options[:privileged] && RESERVED_VARIABLE_NAMES.include?(name)
+ raise Puppet::ParseError, "Attempt to assign to a reserved variable name: '#{name}'"
+ end
+
table = effective_symtable options[:ephemeral]
if table.bound?(name)
if options[:append]
@@ -494,6 +502,29 @@ class Puppet::Parser::Scope
table[name]
end
+ def set_trusted(hash)
+ setvar('trusted', deep_freeze(hash), :privileged => true)
+ end
+
+ # Deeply freezes the given object. The object and its content must be of the types:
+ # Array, Hash, Numeric, Boolean, Symbol, Regexp, NilClass, or String. All other types raises an Error.
+ # (i.e. if they are assignable to Puppet::Pops::Types::Data type).
+ #
+ def deep_freeze(object)
+ case object
+ when Hash
+ object.each {|k, v| deep_freeze(k); deep_freeze(v) }
+ when NilClass
+ # do nothing
+ when String
+ object.freeze
+ else
+ raise Puppet::Error, "Unsupported data type: '#{object.class}"
+ end
+ object
+ end
+ private :deep_freeze
+
# Return the effective "table" for setting variables.
# This method returns the first ephemeral "table" that acts as a local scope, or this
# scope's symtable. If the parameter `use_ephemeral` is true, the "top most" ephemeral "table"
diff --git a/lib/puppet/parser/type_loader.rb b/lib/puppet/parser/type_loader.rb
index 132242f88..4ace9f90a 100644
--- a/lib/puppet/parser/type_loader.rb
+++ b/lib/puppet/parser/type_loader.rb
@@ -7,61 +7,6 @@ class Puppet::Parser::TypeLoader
extend Forwardable
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 manifest files that match a given file glob pattern.
#
# @param pattern [String] the file glob to apply when determining which files
@@ -103,7 +48,6 @@ class Puppet::Parser::TypeLoader
def initialize(env)
self.environment = env
- @loading_helper = Helper.new
end
# Try to load the object with the given fully qualified name.
@@ -148,11 +92,11 @@ class Puppet::Parser::TypeLoader
end
def load_files(modname, files)
+ @loaded ||= {}
loaded_asts = []
- files.each do |file|
- @loading_helper.do_once(file) do
- loaded_asts << parse_file(file)
- end
+ files.reject { |file| @loaded[file] }.each do |file|
+ loaded_asts << parse_file(file)
+ @loaded[file] = true
end
loaded_asts.collect do |ast|
diff --git a/lib/puppet/pops/binder/bindings_loader.rb b/lib/puppet/pops/binder/bindings_loader.rb
index 44c14adec..eca05c1e9 100644
--- a/lib/puppet/pops/binder/bindings_loader.rb
+++ b/lib/puppet/pops/binder/bindings_loader.rb
@@ -35,7 +35,7 @@ class Puppet::Pops::Binder::BindingsLoader
def self.loadable?(basedir, name)
# note, "lib" is added by the autoloader
#
- paths_for_name(name).find {|p| File.exists?(File.join(basedir, "lib/puppet/bindings", p)+'.rb') }
+ paths_for_name(name).find {|p| Puppet::FileSystem::File.exist?(File.join(basedir, "lib/puppet/bindings", p)+'.rb') }
end
private
diff --git a/lib/puppet/pops/binder/config/binder_config.rb b/lib/puppet/pops/binder/config/binder_config.rb
index aa5c45e54..a4fb7796a 100644
--- a/lib/puppet/pops/binder/config/binder_config.rb
+++ b/lib/puppet/pops/binder/config/binder_config.rb
@@ -72,20 +72,20 @@ module Puppet::Pops::Binder::Config
rootdir = confdir
if rootdir.is_a?(String)
expanded_config_file = File.expand_path(File.join(rootdir, '/binder_config.yaml'))
- if File.exist?(expanded_config_file)
+ if Puppet::FileSystem::File.exist?(expanded_config_file)
@config_file = expanded_config_file
end
else
raise ArgumentError, "No Puppet settings 'confdir', or it is not a String"
end
when String
- unless File.exist?(@config_file)
+ unless Puppet::FileSystem::File.exist?(@config_file)
raise ArgumentError, "Cannot find the given binder configuration file '#{@config_file}'"
end
else
raise ArgumentError, "The setting binder_config is expected to be a String, got: #{@config_file.class.name}."
end
- unless @config_file.is_a?(String) && File.exist?(@config_file)
+ unless @config_file.is_a?(String) && Puppet::FileSystem::File.exist?(@config_file)
@config_file = nil # use defaults
end
diff --git a/lib/puppet/pops/binder/hiera2/bindings_provider.rb b/lib/puppet/pops/binder/hiera2/bindings_provider.rb
index 3cfc41aeb..d0e5d9a5e 100644
--- a/lib/puppet/pops/binder/hiera2/bindings_provider.rb
+++ b/lib/puppet/pops/binder/hiera2/bindings_provider.rb
@@ -35,7 +35,7 @@ module Puppet::Pops::Binder::Hiera2
precedence = []
@config.hierarchy.each do |key, value, path|
- source_file = File.join(@config.module_dir, 'hiera.config.yaml')
+ source_file = File.join(@config.module_dir, 'hiera.yaml')
category_value = @parser.evaluate_string(scope, @parser.quote(value), source_file)
hierarchy[key] = {
diff --git a/lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb b/lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb
index 8be9f9018..d2ae152f7 100644
--- a/lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb
+++ b/lib/puppet/pops/binder/scheme_handler/confdir_hiera_scheme.rb
@@ -43,7 +43,7 @@ class Puppet::Pops::Binder::SchemeHandler::ConfdirHieraScheme < Puppetx::Puppet:
end
def config_exist?(uri, composer)
- File.exist?(File.join(composer.confdir, uri.path, 'hiera.yaml'))
+ Puppet::FileSystem::File.exist?(File.join(composer.confdir, uri.path, 'hiera.yaml'))
end
# A hiera.yaml that exists, is readable, can be loaded, and does not have version >= 2 set is ignored.
diff --git a/lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb b/lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb
index 38f57d033..0663ee2cb 100644
--- a/lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb
+++ b/lib/puppet/pops/binder/scheme_handler/module_hiera_scheme.rb
@@ -36,7 +36,7 @@ class Puppet::Pops::Binder::SchemeHandler::ModuleHieraScheme < Puppetx::Puppet::
when '*'
# create new URIs, one per module name that has a hiera.yaml file relative to its root
composer.name_to_module.each_pair do | name, mod |
- if File.exist?(File.join(mod.path, split_path[ 2..-1 ], 'hiera.yaml' ))
+ if Puppet::FileSystem::File.exist?(File.join(mod.path, split_path[ 2..-1 ], 'hiera.yaml' ))
path_parts =["", name] + split_path[2..-1]
result << URI.parse('module-hiera:'+File.join(path_parts))
end
@@ -47,7 +47,7 @@ class Puppet::Pops::Binder::SchemeHandler::ModuleHieraScheme < Puppetx::Puppet::
# If uri has query that is empty, or the text 'optional' skip this uri if it does not exist
if query = uri.query()
if query == '' || query == 'optional'
- if File.exist?(File.join(mod.path, split_path[ 2..-1 ], 'hiera.yaml' ))
+ if Puppet::FileSystem::File.exist?(File.join(mod.path, split_path[ 2..-1 ], 'hiera.yaml' ))
result << URI.parse('module-hiera:' + uri.path)
end
end
diff --git a/lib/puppet/pops/issues.rb b/lib/puppet/pops/issues.rb
index 0276f4022..61663b167 100644
--- a/lib/puppet/pops/issues.rb
+++ b/lib/puppet/pops/issues.rb
@@ -195,6 +195,10 @@ module Puppet::Pops::Issues
"Illegal +> operation on attribute #{name}. This operator can not be used in #{label.a_an(parent)}"
end
+ ILLEGAL_NAME = hard_issue :ILLEGAL_NAME, :name do
+ "Illegal name. The given name #{name} does not conform to the naming rule \\A((::)?[a-z0-9]\w*)(::[a-z0-9]\w*)*\\z"
+ end
+
# In case a model is constructed programmatically, it must create valid type references.
#
ILLEGAL_CLASSREF = hard_issue :ILLEGAL_CLASSREF, :name do
diff --git a/lib/puppet/pops/model/ast_transformer.rb b/lib/puppet/pops/model/ast_transformer.rb
index b16d951bc..2d4504050 100644
--- a/lib/puppet/pops/model/ast_transformer.rb
+++ b/lib/puppet/pops/model/ast_transformer.rb
@@ -439,7 +439,10 @@ class Puppet::Pops::Model::AstTransformer
args = {
:code => transform(o.body)
}
- args[:parent] = transform(o.parent) unless is_nop?(o.parent)
+ args[:parent] = hostname(o.parent) unless is_nop?(o.parent)
+ if(args[:parent].is_a?(Array))
+ raise "Illegal expression - unacceptable as a node parent"
+ end
Puppet::Parser::AST::Node.new(hostname(o.host_matches), merge_location(args, o))
end
diff --git a/lib/puppet/pops/model/model_label_provider.rb b/lib/puppet/pops/model/model_label_provider.rb
index b48e7b441..469130de4 100644
--- a/lib/puppet/pops/model/model_label_provider.rb
+++ b/lib/puppet/pops/model/model_label_provider.rb
@@ -15,7 +15,7 @@ class Puppet::Pops::Model::ModelLabelProvider < Puppet::Pops::LabelProvider
def label_Factory o ; label(o.current) end
def label_Array o ; "Array Object" end
def label_LiteralNumber o ; "Literal Number" end
- def label_ArithmeticExpression o ; "'#{o_operator}' expression" end
+ def label_ArithmeticExpression o ; "'#{o.operator}' expression" end
def label_AccessExpression o ; "'[]' expression" end
def label_MatchExpression o ; "'#{o.operator}' expression" end
def label_CollectExpression o ; label(o.query) end
diff --git a/lib/puppet/pops/parser/egrammar.ra b/lib/puppet/pops/parser/egrammar.ra
index eb8357d58..b45cd2cc3 100644
--- a/lib/puppet/pops/parser/egrammar.ra
+++ b/lib/puppet/pops/parser/egrammar.ra
@@ -14,7 +14,7 @@ token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSREF
token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS
token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
token IN UNLESS PIPE
-token LAMBDA SELBRACE
+token SELBRACE
token LOW
prechigh
@@ -203,14 +203,11 @@ call_method_with_lambda_expression
#
# This is a temporary switch while experimenting with concrete syntax
# One should be picked for inclusion in puppet.
-lambda
- : lambda_j8
- | lambda_ruby
-# Java8-like lambda with parameters to the left of the body
-lambda_j8
- : lambda_parameter_list optional_farrow lambda_rest {
- result = Factory.LAMBDA(val[0], val[2])
+# Lambda with parameters to the left of the body
+lambda
+ : lambda_parameter_list lambda_rest {
+ result = Factory.LAMBDA(val[0], val[1])
# loc result, val[1] # TODO
}
@@ -218,22 +215,6 @@ lambda_rest
: LBRACE statements RBRACE { result = val[1] }
| LBRACE RBRACE { result = nil }
-optional_farrow
- : nil
- | FARROW
-
-# Ruby-like lambda with parameters inside the body
-#
-lambda_ruby
- : LAMBDA lambda_parameter_list statements RBRACE {
- result = Factory.LAMBDA(val[1], val[2])
- loc result, val[0], val[3]
- }
- | LAMBDA lambda_parameter_list RBRACE {
- result = Factory.LAMBDA(val[1], nil)
- loc result, val[0], val[2]
- }
-
# Produces Array<Model::Parameter>
lambda_parameter_list
: PIPE PIPE { result = [] }
diff --git a/lib/puppet/pops/parser/eparser.rb b/lib/puppet/pops/parser/eparser.rb
index e476e0358..6d71e7b7e 100644
--- a/lib/puppet/pops/parser/eparser.rb
+++ b/lib/puppet/pops/parser/eparser.rb
@@ -20,7 +20,7 @@ module Puppet
module Parser
class Parser < Racc::Parser
-module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 718)
+module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 699)
# Make emacs happy
# Local Variables:
@@ -30,125 +30,64 @@ module_eval(<<'...end egrammar.ra/module_eval...', 'egrammar.ra', 718)
##### State transition tables begin ###
clist = [
-'68,-132,112,207,220,235,344,51,53,87,88,84,79,90,234,94,-130,89,51,53',
-'80,82,81,83,234,283,224,269,222,115,233,223,321,114,207,234,244,204',
-'93,-198,-207,-132,86,85,54,299,72,73,75,74,77,78,68,70,71,54,-130,316',
-'202,315,69,87,88,84,79,90,59,94,76,89,51,53,80,82,81,83,245,59,115,-198',
-'-207,301,114,115,115,51,53,114,114,115,93,330,291,114,86,85,105,104',
-'72,73,75,74,77,78,68,70,71,120,225,227,122,226,69,87,88,84,79,90,68',
-'94,76,89,54,297,80,82,81,83,68,59,115,90,268,94,114,89,243,51,53,105',
-'104,90,93,94,128,89,86,85,68,267,72,73,75,74,77,78,93,70,71,84,79,90',
-'68,94,69,89,93,306,80,82,81,83,76,192,54,90,316,94,315,89,309,70,71',
-'105,104,310,93,207,69,51,53,85,68,168,72,73,75,74,77,78,93,70,71,84',
-'79,90,313,94,69,89,51,53,80,82,81,83,76,105,68,68,317,51,53,229,228',
-'319,120,63,263,122,93,90,90,94,94,89,89,241,72,73,75,74,77,78,243,70',
-'71,120,59,326,122,327,69,68,241,91,93,93,120,267,76,122,87,88,84,79',
-'90,259,94,59,89,70,71,80,82,81,83,68,69,63,59,64,66,65,67,134,258,257',
-'336,79,90,93,94,243,89,86,85,80,243,72,73,75,74,77,78,116,70,71,241',
-'339,217,341,106,69,217,93,282,286,319,346,347,76,348,72,73,75,74,77',
-'78,68,70,71,349,99,352,353,354,69,285,63,60,79,90,361,94,76,89,362,363',
-'80,364,,,68,,,,,,,,,,,,79,90,93,94,,89,,,80,,72,73,75,74,77,78,68,70',
-'71,,,,,,69,,93,,,90,,94,76,89,72,73,75,74,77,78,68,70,71,,,,,,69,,,',
-'79,90,93,94,76,89,,,80,,72,73,75,74,77,78,68,70,71,,,,,,69,,93,,,90',
-',94,76,89,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,93,94,76',
-'89,,,80,82,81,83,68,,,,,70,71,,,,,,69,90,93,94,,89,86,85,,,72,73,75',
-'74,77,78,68,70,71,,,,,,69,,93,,,90,,94,76,89,72,73,75,74,77,78,68,70',
-'71,,,,,,69,,,,,90,93,94,76,89,,,,,72,73,75,74,,,,70,71,,,,,,69,,93,',
-',,,,76,,72,73,75,74,,,68,70,71,,,,,,69,87,88,84,79,90,239,94,76,89,',
-',80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,68,70,71',
-',,,,,69,87,88,84,79,90,,94,76,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86',
-'85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,,94,76,89,68',
-',80,82,81,83,,,,,,,,90,,94,,89,,,93,,,,86,85,,,72,73,75,74,77,78,,70',
-'71,,93,,,,69,,,,,,75,74,76,,68,70,71,,,,,,69,87,88,84,79,90,,94,76,89',
-'68,,80,82,81,83,,,,,,,,90,,94,,89,,,93,,,,86,85,,,72,73,75,74,77,78',
-',70,71,,93,,,,69,,,,,,75,74,76,,68,70,71,,,,,,69,87,88,84,79,90,,94',
-'76,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,68',
-'70,71,,,,,,69,87,88,84,79,90,,94,76,89,,,80,82,81,83,,,,,,,,,,,,,,,93',
-',,,86,85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,,94,76',
-'89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,68,70',
-'71,,,,,,69,87,88,84,79,90,,94,76,89,,,80,82,81,83,,,,,,,,,,,,,,,93,',
-',,86,85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90,,94,76,89',
+'68,215,228,229,-126,230,202,229,238,87,88,84,79,90,290,94,262,89,-124',
+'68,80,82,81,83,68,217,51,53,51,53,224,223,90,239,94,-192,89,90,93,94',
+'199,89,86,85,-126,212,72,73,75,74,77,78,276,70,71,51,53,93,-124,68,69',
+'202,93,117,-201,54,119,76,87,88,84,79,90,240,94,-192,89,70,71,80,82',
+'81,83,68,69,59,229,59,51,53,292,212,117,109,312,119,90,93,94,112,89',
+'86,85,111,-201,72,73,75,74,77,78,294,70,71,59,51,53,279,68,69,112,93',
+'51,53,111,54,76,87,88,84,79,90,307,94,306,89,70,71,80,82,81,83,112,69',
+'112,219,111,59,111,321,218,278,117,112,112,119,93,111,111,117,86,85',
+'119,275,72,73,75,74,77,78,220,70,71,221,59,68,68,307,69,306,238,59,189',
+'299,300,76,84,79,90,90,94,94,89,89,301,80,82,81,83,51,53,202,68,165',
+'51,53,284,64,66,65,67,125,304,93,93,90,236,94,85,89,308,72,73,75,74',
+'77,78,310,70,71,222,261,68,236,238,69,54,317,318,260,93,54,76,84,79',
+'90,260,94,63,89,63,131,80,82,81,83,68,102,254,327,253,198,113,252,330',
+'102,103,238,102,90,93,94,334,89,310,336,337,338,72,73,75,74,77,78,339',
+'70,71,99,342,343,344,68,69,91,93,236,63,60,351,76,87,88,84,79,90,352',
+'94,353,89,70,71,80,82,81,83,68,69,354,,,,,,,,,,79,90,93,94,,89,86,85',
+'80,,72,73,75,74,77,78,,70,71,,,,,,69,,93,,,,,76,68,,72,73,75,74,77,78',
+',70,71,,79,90,,94,69,89,,,80,,,76,68,,,,,,,,,,,,79,90,93,94,,89,,,80',
+',72,73,75,74,77,78,,70,71,,,,,,69,,93,,,,,76,68,,72,73,75,74,77,78,',
+'70,71,,79,90,,94,69,89,,,80,,,76,68,,,,,,,,,,,,,90,93,94,,89,,,,,72',
+'73,75,74,77,78,,70,71,,,,,,69,,93,,,,,76,,,72,73,75,74,77,78,,70,71',
+',,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,68,,,,,,,,,',
+',,,90,93,94,,89,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,,93,,,,68,76',
+',,72,73,75,74,77,78,,70,71,90,,94,,89,69,,,,,,68,76,,,,,,,,,,,,90,93',
+'94,,89,,,,,72,73,75,74,,,,70,71,,,,,,69,,93,,,,,76,,,72,73,75,74,,,',
+'70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,234,94,,89,,,80,82,81,83,,,',
+',,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76',
+'87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72',
+'73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80',
+'82,81,83,68,,,,,,,,,,,,,90,93,94,,89,86,85,,,72,73,75,74,77,78,,70,71',
+',,,,,69,,93,,,,,76,,,,,75,74,,,,70,71,,,,,68,69,,,,,,,76,87,88,84,79',
+'90,,94,,89,,,80,82,81,83,68,,,,,,,,,,,,,90,93,94,,89,86,85,,,72,73,75',
+'74,77,78,,70,71,,,,,,69,,93,,,,,76,,,,,75,74,,,,70,71,,,,,68,69,,,,',
+',,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85',
+',,72,73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89',
',,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,',
-',,,,69,68,,213,,,,,76,,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,',
-',,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,,69,68,,212,,,,,76',
-',87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72',
-'73,75,74,77,78,,70,71,,,,,,69,68,,211,,,,,76,,87,88,84,79,90,,94,,89',
-',,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,',
-',,,,69,68,,210,,,,,76,,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,',
-',,,,,,93,,,,86,85,,,72,73,75,74,77,78,68,70,71,,,,,,69,87,88,84,79,90',
-',94,76,89,,197,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77',
-'78,,70,71,51,53,,,47,69,48,,,,,,,76,,,,,,,,,13,,,,,,38,,44,,46,96,,45',
-'58,54,,40,57,,,,55,12,,,56,51,53,11,,47,,48,,,,59,,,,,,39,,,167,,,13',
-',,,,,170,187,181,188,46,182,190,183,179,177,,172,185,,,,55,12,191,186',
-'184,51,53,11,,47,,48,333,,,59,,,,,189,171,,,,,,13,,,,,,38,,44,,46,42',
-',45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,322,,,,,,59,,,,,,39,',
-',13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
-'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
-'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
-',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
-',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
-'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
-'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
-',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
-',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
-'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
-'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
-',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
-',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
-'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
-'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
-',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
-',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
-'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
-'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
-',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
-',,13,,,,,,170,187,181,188,46,182,190,183,179,177,,172,185,,,,55,12,191',
-'186,184,51,53,11,,47,,48,308,,,59,,,,,189,171,,,,,,13,,,,,,38,,44,,46',
-'42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,',
-',13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
-'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
-'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
-',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
-',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12',
-'51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,199,,,,,38,,44,,46,96,,45,58',
-'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38',
-',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,',
-',,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47',
-'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55',
-'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,209,,,,,38,,44,,46,96,,45',
-'58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,',
-'38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59',
-',,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56',
-',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57',
-'43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
-'42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,',
-',13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
-',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
-'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
-'40,57,,,,55,12,,,56,51,53,11,,47,290,48,,,,59,,,,,,39,,,,,,13,,,,,,38',
-',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,335,,,,,,59',
-',,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56',
-',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57',
-',,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96',
-',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,273,,,,,,59,,,,,,39,,,13',
-',,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,',
-',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51',
-'53,56,,47,11,48,271,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54',
-',40,57,43,,,55,12,51,53,56,,47,11,48,265,,,,,,59,,,,,,39,,,13,,,,,,38',
-',44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,',
-',,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47',
-'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55',
-'12,,,56,51,53,11,,47,126,48,,,,59,,,,,,39,,,,,,13,,,,,,38,,44,,46,96',
-',45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,',
-',,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,',
-',59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
+',,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,',
+',93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84',
+'79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74',
+'77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83',
+',,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,208,',
+',,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85',
+',,72,73,75,74,77,78,,70,71,,,,,68,69,207,,,,,,76,87,88,84,79,90,,94',
+',89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70',
+'71,,,,,68,69,206,,,,,,76,87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,',
+',,,,,,,,93,,,,86,85,,,72,73,75,74,77,78,,70,71,,,,,68,69,205,,,,,,76',
+'87,88,84,79,90,,94,,89,,,80,82,81,83,,,,,,,,,,,,,,,93,,,,86,85,,,72',
+'73,75,74,77,78,,70,71,,,,,68,69,,,,,,,76,87,88,84,79,90,,94,,89,,194',
+'80,82,81,83,,,,,,,,,,51,53,,,47,93,48,,,86,85,,,72,73,75,74,77,78,,70',
+'71,13,,,,,69,38,,44,,46,96,76,45,58,54,,40,57,,,,55,12,51,53,56,,47',
+'11,48,,,,,,,59,,,,,,39,,164,13,,,,,,167,184,178,185,46,179,187,180,176',
+'174,,169,182,,,,55,12,188,183,181,51,53,11,,47,,48,324,,,59,,,,,186',
+'168,,,,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56',
+',47,11,48,313,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
+'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13',
+',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,',
+',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13',
@@ -156,24 +95,85 @@ clist = [
',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
-'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,351,,,,,,59,,,,,,39',
-',,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13',
+',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,',
+',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
+'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
+'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13',
+',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,',
+',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
+'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
+'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13',
+',,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,',
+',,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53',
+'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,167,184,178,185,46,179,187,180',
+'176,174,,169,182,,,,55,12,188,183,181,51,53,11,,47,,48,,,,59,,,,,186',
+'168,,,,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47',
+'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55',
+'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58',
+'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38',
+',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,',
+',,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47',
+'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55',
+'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58',
+'54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,196,,',
+',,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59',
+',,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,',
+'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,',
+',,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,204,,,,,38,,44,,46',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13',
+',,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,',
+',,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,',
+'40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,',
+'39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11',
'48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12',
'51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54',
-',40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
-',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,356,,,,,,59,,,,',
-',39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47',
-'11,48,358,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43',
-',,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42',
-',45,58,54,61,40,57,43,,,55,12,51,53,56,,47,11,48,360,,,,,,59,,,,,,39',
+',40,57,,,,55,12,,,56,51,53,11,,47,283,48,,,,59,,,,,,39,,,,,,13,,,,,',
+'38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,326,,,,,',
+'59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53',
+'56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40',
+'57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46',
+'96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,266,,,,,,59,,,,,,39',
',,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11',
'48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55',
'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58',
-'54,,40,57,,,,55,12,,,56,,,11,,,,253,187,252,188,59,250,190,254,248,247',
-'39,249,251,,,,,,191,186,255,253,187,252,188,,250,190,254,248,247,,249',
-'251,,,189,256,,191,186,255,253,187,252,188,,250,190,254,248,247,,249',
-'251,,,189,256,,191,186,255,,,,,,,,,,,,,,,,189,256' ]
- racc_action_table = arr = ::Array.new(4804, nil)
+'54,,40,57,,,,55,12,51,53,56,,47,11,48,258,,,,,,59,,,,,,39,,,13,,,,,',
+'38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59',
+',,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,',
+'47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,',
+',,55,12,,,56,51,53,11,,47,123,48,,,,59,,,,,,39,,,,,,13,,,,,,38,,44,',
+'46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39',
+',,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48',
+',,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55,12,51',
+'53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,',
+'40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44',
+',46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,341,,,,,,59,,,,',
+',39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56,,47',
+'11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58,54,,40,57,,,,55',
+'12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,96,,45,58',
+'54,,40,57,,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38',
+',44,,46,96,,45,58,54,,40,57,,,,55,12,51,53,56,,47,11,48,346,,,,,,59',
+',,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56',
+',47,11,48,348,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40',
+'57,43,,,55,12,51,53,56,,47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,',
+'46,42,,45,58,54,61,40,57,43,,,55,12,51,53,56,,47,11,48,350,,,,,,59,',
+',,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57,43,,,55,12,51,53,56',
+',47,11,48,,,,,,,59,,,,,,39,,,13,,,,,,38,,44,,46,42,,45,58,54,,40,57',
+'43,,,55,12,51,53,56,,47,11,48,264,,,,,,59,,,,,,39,,,13,,,,,,38,,44,',
+'46,42,,45,58,54,,40,57,43,,,55,12,,,56,,,11,,,,248,184,247,185,59,245',
+'187,249,243,242,39,244,246,,,,,,188,183,250,248,184,247,185,,245,187',
+'249,243,242,,244,246,,,186,251,,188,183,250,248,184,247,185,,245,187',
+'249,243,242,,244,246,,,186,251,,188,183,250,,,,,,,,,,,,,,,,186,251' ]
+ racc_action_table = arr = ::Array.new(4874, nil)
idx = 0
clist.each do |str|
str.split(',', -1).each do |i|
@@ -183,167 +183,167 @@ clist = [
end
clist = [
-'164,179,42,105,118,164,316,70,70,164,164,164,164,164,208,164,177,164',
-'71,71,164,164,164,164,274,217,125,208,118,42,141,125,274,42,217,141',
-'180,105,164,185,184,179,164,164,70,240,164,164,164,164,164,164,163,164',
-'164,71,177,271,103,271,164,163,163,163,163,163,70,163,164,163,220,220',
-'163,163,163,163,180,71,96,185,184,242,96,182,282,226,226,182,282,181',
-'163,282,226,181,163,163,306,306,163,163,163,163,163,163,162,163,163',
-'220,127,130,220,127,163,162,162,162,162,162,97,162,163,162,226,236,162',
-'162,162,162,151,220,44,97,207,97,44,97,246,48,48,199,199,151,162,151',
-'48,151,162,162,161,205,162,162,162,162,162,162,97,162,162,161,161,161',
-'142,161,162,161,151,260,161,161,161,161,162,92,48,142,313,142,313,142',
-'264,151,151,36,36,266,161,267,151,183,183,161,160,90,161,161,161,161',
-'161,161,142,161,161,160,160,160,270,160,161,160,45,45,160,160,160,160',
-'161,104,150,95,272,222,222,133,133,273,183,135,200,183,160,150,95,150',
-'95,150,95,277,160,160,160,160,160,160,278,160,160,45,183,279,45,280',
-'160,10,214,10,150,95,222,284,160,222,10,10,10,10,10,198,10,45,10,150',
-'150,10,10,10,10,159,150,62,222,7,7,7,7,60,196,194,296,159,159,10,159',
-'174,159,10,10,159,298,10,10,10,10,10,10,43,10,10,173,305,113,307,37',
-'10,117,159,215,219,317,319,320,10,324,159,159,159,159,159,159,158,159',
-'159,325,35,331,332,334,159,218,5,1,158,158,350,158,159,158,355,357,158',
-'359,,,157,,,,,,,,,,,,157,157,158,157,,157,,,157,,158,158,158,158,158',
-'158,155,158,158,,,,,,158,,157,,,155,,155,158,155,157,157,157,157,157',
-'157,156,157,157,,,,,,157,,,,156,156,155,156,157,156,,,156,,155,155,155',
-'155,155,155,149,155,155,,,,,,155,,156,,,149,,149,155,149,156,156,156',
-'156,156,156,312,156,156,,,,,,156,312,312,312,312,312,149,312,156,312',
-',,312,312,312,312,154,,,,,149,149,,,,,,149,154,312,154,,154,312,312',
-',,312,312,312,312,312,312,153,312,312,,,,,,312,,154,,,153,,153,312,153',
-'154,154,154,154,154,154,152,154,154,,,,,,154,,,,,152,153,152,154,152',
-',,,,153,153,153,153,,,,153,153,,,,,,153,,152,,,,,,153,,152,152,152,152',
-',,169,152,152,,,,,,152,169,169,169,169,169,169,169,152,169,,,169,169',
-'169,169,,,,,,,,,,,,,,,169,,,,169,169,,,169,169,169,169,169,169,304,169',
-'169,,,,,,169,304,304,304,304,304,,304,169,304,,,304,304,304,304,,,,',
-',,,,,,,,,,304,,,,304,304,,,304,304,304,304,304,304,303,304,304,,,,,',
-'304,303,303,303,303,303,,303,304,303,148,,303,303,303,303,,,,,,,,148',
-',148,,148,,,303,,,,303,303,,,303,303,303,303,303,303,,303,303,,148,',
-',,303,,,,,,148,148,303,,295,148,148,,,,,,148,295,295,295,295,295,,295',
-'148,295,147,,295,295,295,295,,,,,,,,147,,147,,147,,,295,,,,295,295,',
-',295,295,295,295,295,295,,295,295,,147,,,,295,,,,,,147,147,295,,293',
-'147,147,,,,,,147,293,293,293,293,293,,293,147,293,,,293,293,293,293',
-',,,,,,,,,,,,,,293,,,,293,293,,,293,293,293,293,293,293,193,293,293,',
-',,,,293,193,193,193,193,193,,193,293,193,,,193,193,193,193,,,,,,,,,',
-',,,,,193,,,,193,193,,,193,193,193,193,193,193,289,193,193,,,,,,193,289',
-'289,289,289,289,,289,193,289,,,289,289,289,289,,,,,,,,,,,,,,,289,,,',
-'289,289,,,289,289,289,289,289,289,131,289,289,,,,,,289,131,131,131,131',
-'131,,131,289,131,,,131,131,131,131,,,,,,,,,,,,,,,131,,,,131,131,,,131',
-'131,131,131,131,131,124,131,131,,,,,,131,124,124,124,124,124,,124,131',
-'124,,,124,124,124,124,,,,,,,,,,,,,,,124,,,,124,124,,,124,124,124,124',
-'124,124,,124,124,,,,,,124,111,,111,,,,,124,,111,111,111,111,111,,111',
-',111,,,111,111,111,111,,,,,,,,,,,,,,,111,,,,111,111,,,111,111,111,111',
-'111,111,,111,111,,,,,,111,110,,110,,,,,111,,110,110,110,110,110,,110',
-',110,,,110,110,110,110,,,,,,,,,,,,,,,110,,,,110,110,,,110,110,110,110',
-'110,110,,110,110,,,,,,110,109,,109,,,,,110,,109,109,109,109,109,,109',
-',109,,,109,109,109,109,,,,,,,,,,,,,,,109,,,,109,109,,,109,109,109,109',
-'109,109,,109,109,,,,,,109,107,,107,,,,,109,,107,107,107,107,107,,107',
-',107,,,107,107,107,107,,,,,,,,,,,,,,,107,,,,107,107,,,107,107,107,107',
-'107,107,98,107,107,,,,,,107,98,98,98,98,98,,98,107,98,,98,98,98,98,98',
-',,,,,,,,,,,,,,98,,,,98,98,,,98,98,98,98,98,98,,98,98,89,89,,,89,98,89',
-',,,,,,98,,,,,,,,,89,,,,,,89,,89,,89,89,,89,89,89,,89,89,,,,89,89,,,89',
-'213,213,89,,213,,213,,,,89,,,,,,89,,,89,,,213,,,,,,213,213,213,213,213',
-'213,213,213,213,213,,213,213,,,,213,213,213,213,213,285,285,213,,285',
-',285,285,,,213,,,,,213,213,,,,,,285,,,,,,285,,285,,285,285,,285,285',
-'285,,285,285,285,,,285,285,275,275,285,,275,285,275,275,,,,,,285,,,',
-',,285,,,275,,,,,,275,,275,,275,275,,275,275,275,,275,275,,,,275,275',
-'72,72,275,,72,275,72,,,,,,,275,,,,,,275,,,72,,,,,,72,,72,,72,72,,72',
-'72,72,,72,72,,,,72,72,73,73,72,,73,72,73,,,,,,,72,,,,,,72,,,73,,,,,',
-'73,,73,,73,73,,73,73,73,,73,73,,,,73,73,74,74,73,,74,73,74,,,,,,,73',
-',,,,,73,,,74,,,,,,74,,74,,74,74,,74,74,74,,74,74,,,,74,74,75,75,74,',
-'75,74,75,,,,,,,74,,,,,,74,,,75,,,,,,75,,75,,75,75,,75,75,75,,75,75,',
-',,75,75,76,76,75,,76,75,76,,,,,,,75,,,,,,75,,,76,,,,,,76,,76,,76,76',
-',76,76,76,,76,76,,,,76,76,77,77,76,,77,76,77,,,,,,,76,,,,,,76,,,77,',
-',,,,77,,77,,77,77,,77,77,77,,77,77,,,,77,77,78,78,77,,78,77,78,,,,,',
-',77,,,,,,77,,,78,,,,,,78,,78,,78,78,,78,78,78,,78,78,,,,78,78,79,79',
-'78,,79,78,79,,,,,,,78,,,,,,78,,,79,,,,,,79,,79,,79,79,,79,79,79,,79',
-'79,,,,79,79,80,80,79,,80,79,80,,,,,,,79,,,,,,79,,,80,,,,,,80,,80,,80',
-'80,,80,80,80,,80,80,,,,80,80,81,81,80,,81,80,81,,,,,,,80,,,,,,80,,,81',
-',,,,,81,,81,,81,81,,81,81,81,,81,81,,,,81,81,82,82,81,,82,81,82,,,,',
-',,81,,,,,,81,,,82,,,,,,82,,82,,82,82,,82,82,82,,82,82,,,,82,82,83,83',
-'82,,83,82,83,,,,,,,82,,,,,,82,,,83,,,,,,83,,83,,83,83,,83,83,83,,83',
-'83,,,,83,83,84,84,83,,84,83,84,,,,,,,83,,,,,,83,,,84,,,,,,84,,84,,84',
-'84,,84,84,84,,84,84,,,,84,84,85,85,84,,85,84,85,,,,,,,84,,,,,,84,,,85',
-',,,,,85,,85,,85,85,,85,85,85,,85,85,,,,85,85,86,86,85,,86,85,86,,,,',
-',,85,,,,,,85,,,86,,,,,,86,,86,,86,86,,86,86,86,,86,86,,,,86,86,167,167',
-'86,,167,86,167,,,,,,,86,,,,,,86,,,167,,,,,,167,,167,,167,167,,167,167',
-'167,,167,167,,,,167,167,88,88,167,,88,167,88,,,,,,,167,,,,,,167,,,88',
-',,,,,88,,88,,88,88,,88,88,88,,88,88,,,,88,88,68,68,88,,68,88,68,,,,',
-',,88,,,,,,88,,,68,,,,,,68,,68,,68,68,,68,68,68,,68,68,,,,68,68,268,268',
-'68,,268,68,268,,,,,,,68,,,,,,68,,,268,,,,,,268,,268,,268,268,,268,268',
-'268,,268,268,,,,268,268,91,91,268,,91,268,91,,,,,,,268,,,,,,268,,,91',
-',,,,,91,91,91,91,91,91,91,91,91,91,,91,91,,,,91,91,91,91,91,263,263',
-'91,,263,,263,263,,,91,,,,,91,91,,,,,,263,,,,,,263,,263,,263,263,,263',
-'263,263,,263,263,263,,,263,263,93,93,263,,93,263,93,,,,,,,263,,,,,,263',
-',,93,,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,93,93,94,94,93,,94,93,94',
-',,,,,,93,,,,,,93,,,94,,,,,,94,,94,,94,94,,94,94,94,,94,94,,,,94,94,259',
-'259,94,,259,94,259,,,,,,,94,,,,,,94,,,259,,,,,,259,,259,,259,259,,259',
-'259,259,,259,259,,,,259,259,245,245,259,,245,259,245,,,,,,,259,,,,,',
-'259,,,245,,,,,,245,,245,,245,245,,245,245,245,,245,245,,,,245,245,244',
-'244,245,,244,245,244,,,,,,,245,,,,,,245,,,244,,,,,,244,,244,,244,244',
-',244,244,244,,244,244,,,,244,244,67,67,244,,67,244,67,,,,,,,244,,,,',
-',244,,,67,,,,,,67,,67,,67,67,,67,67,67,,67,67,67,,,67,67,99,99,67,,99',
-'67,99,,,,,,,67,,,,,,67,,,99,99,,,,,99,,99,,99,99,,99,99,99,,99,99,,',
-',99,99,241,241,99,,241,99,241,,,,,,,99,,,,,,99,,,241,,,,,,241,,241,',
-'241,241,,241,241,241,,241,241,,,,241,241,235,235,241,,235,241,235,,',
-',,,,241,,,,,,241,,,235,,,,,,235,,235,,235,235,,235,235,235,,235,235',
-',,,235,235,234,234,235,,234,235,234,,,,,,,235,,,,,,235,,,234,,,,,,234',
-',234,,234,234,,234,234,234,,234,234,,,,234,234,106,106,234,,106,234',
-'106,,,,,,,234,,,,,,234,,,106,106,,,,,106,,106,,106,106,,106,106,106',
-',106,106,,,,106,106,66,66,106,,66,106,66,,,,,,,106,,,,,,106,,,66,,,',
-',,66,,66,,66,66,,66,66,66,,66,66,66,,,66,66,65,65,66,,65,66,65,,,,,',
-',66,,,,,,66,,,65,,,,,,65,,65,,65,65,,65,65,65,,65,65,65,,,65,65,64,64',
-'65,,64,65,64,,,,,,,65,,,,,,65,,,64,,,,,,64,,64,,64,64,,64,64,64,,64',
-'64,64,,,64,64,63,63,64,,63,64,63,,,,,,,64,,,,,,64,,,63,,,,,,63,,63,',
-'63,63,,63,63,63,,63,63,63,,,63,63,112,112,63,,112,63,112,,,,,,,63,,',
-',,,63,,,112,,,,,,112,,112,,112,112,,112,112,112,,112,112,,,,112,112',
-'232,232,112,,232,112,232,,,,,,,112,,,,,,112,,,232,,,,,,232,,232,,232',
-'232,,232,232,232,,232,232,,,,232,232,227,227,232,,227,232,227,,,,,,',
-'232,,,,,,232,,,227,,,,,,227,,227,,227,227,,227,227,227,,227,227,,,,227',
-'227,,,227,223,223,227,,223,223,223,,,,227,,,,,,227,,,,,,223,,,,,,223',
-',223,,223,223,,223,223,223,,223,223,,,,223,223,286,286,223,,286,223',
-'286,286,,,,,,223,,,,,,223,,,286,,,,,,286,,286,,286,286,,286,286,286',
-',286,286,286,,,286,286,69,69,286,,69,286,69,,,,,,,286,,,,,,286,,,69',
-',,,,,69,,69,,69,69,,69,69,69,,69,69,,,,69,69,212,212,69,,212,69,212',
-',,,,,,69,,,,,,69,,,212,,,,,,212,,212,,212,212,,212,212,212,,212,212',
-',,,212,212,211,211,212,,211,212,211,211,,,,,,212,,,,,,212,,,211,,,,',
-',211,,211,,211,211,,211,211,211,,211,211,211,,,211,211,61,61,211,,61',
-'211,61,,,,,,,211,,,,,,211,,,61,,,,,,61,,61,,61,61,,61,61,61,,61,61,61',
-',,61,61,210,210,61,,210,61,210,210,,,,,,61,,,,,,61,,,210,,,,,,210,,210',
-',210,210,,210,210,210,,210,210,210,,,210,210,203,203,210,,203,210,203',
-'203,,,,,,210,,,,,,210,,,203,,,,,,203,,203,,203,203,,203,203,203,,203',
-'203,203,,,203,203,52,52,203,,52,203,52,,,,,,,203,,,,,,203,,,52,,,,,',
-'52,,52,,52,52,,52,52,52,,52,52,,,,52,52,172,172,52,,172,52,172,,,,,',
-',52,,,,,,52,,,172,,,,,,172,,172,,172,172,,172,172,172,,172,172,,,,172',
-'172,,,172,47,47,172,,47,47,47,,,,172,,,,,,172,,,,,,47,,,,,,47,,47,,47',
-'47,,47,47,47,,47,47,,,,47,47,297,297,47,,297,47,297,,,,,,,47,,,,,,47',
-',,297,,,,,,297,,297,,297,297,,297,297,297,,297,297,,,,297,297,171,171',
-'297,,171,297,171,,,,,,,297,,,,,,297,,,171,,,,,,171,,171,,171,171,,171',
-'171,171,,171,171,,,,171,171,170,170,171,,170,171,170,,,,,,,171,,,,,',
-'171,,,170,,,,,,170,,170,,170,170,,170,170,170,,170,170,,,,170,170,41',
-'41,170,,41,170,41,,,,,,,170,,,,,,170,,,41,,,,,,41,,41,,41,41,,41,41',
-'41,,41,41,,,,41,41,40,40,41,,40,41,40,,,,,,,41,,,,,,41,,,40,,,,,,40',
-',40,,40,40,,40,40,40,,40,40,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,',
-',,40,,,39,,,,,,39,,39,,39,39,,39,39,39,,39,39,,,,39,39,38,38,39,,38',
-'39,38,,,,,,,39,,,,,,39,,,38,,,,,,38,,38,,38,38,,38,38,38,,38,38,,,,38',
-'38,315,315,38,,315,38,315,,,,,,,38,,,,,,38,,,315,,,,,,315,,315,,315',
-'315,,315,315,315,,315,315,,,,315,315,327,327,315,,327,315,327,327,,',
-',,,315,,,,,,315,,,327,,,,,,327,,327,,327,327,,327,327,327,,327,327,327',
-',,327,327,13,13,327,,13,327,13,,,,,,,327,,,,,,327,,,13,,,,,,13,,13,',
-'13,13,,13,13,13,,13,13,,,,13,13,12,12,13,,12,13,12,,,,,,,13,,,,,,13',
-',,12,,,,,,12,,12,,12,12,,12,12,12,,12,12,,,,12,12,11,11,12,,11,12,11',
-',,,,,,12,,,,,,12,,,11,,,,,,11,,11,,11,11,,11,11,11,,11,11,,,,11,11,344',
-'344,11,,344,11,344,344,,,,,,11,,,,,,11,,,344,,,,,,344,,344,,344,344',
-',344,344,344,,344,344,344,,,344,344,346,346,344,,346,344,346,346,,,',
-',,344,,,,,,344,,,346,,,,,,346,,346,,346,346,,346,346,346,,346,346,346',
-',,346,346,4,4,346,,4,346,4,,,,,,,346,,,,,,346,,,4,,,,,,4,,4,,4,4,,4',
-'4,4,4,4,4,4,,,4,4,347,347,4,,347,4,347,347,,,,,,4,,,,,,4,,,347,,,,,',
-'347,,347,,347,347,,347,347,347,,347,347,347,,,347,347,0,0,347,,0,347',
-'0,,,,,,,347,,,,,,347,,,0,,,,,,0,,0,,0,0,,0,0,0,,0,0,0,,,0,0,87,87,0',
-',87,0,87,,,,,,,0,,,,,,0,,,87,,,,,,87,,87,,87,87,,87,87,87,,87,87,,,',
-'87,87,,,87,,,87,,,,192,192,192,192,87,192,192,192,192,192,87,192,192',
-',,,,,192,192,192,238,238,238,238,,238,238,238,238,238,,238,238,,,192',
-'192,,238,238,238,243,243,243,243,,243,243,243,243,243,,243,243,,,238',
-'238,,243,243,243,,,,,,,,,,,,,,,,243,243' ]
- racc_action_check = arr = ::Array.new(4804, nil)
+'161,115,138,203,176,161,102,138,291,161,161,161,161,161,231,161,203',
+'161,174,139,161,161,161,161,148,115,215,215,71,71,130,130,139,177,139',
+'182,139,148,161,148,102,148,161,161,176,110,161,161,161,161,161,161',
+'212,161,161,217,217,139,174,160,161,212,148,215,181,71,215,161,160,160',
+'160,160,160,177,160,182,160,148,148,160,160,160,160,147,148,215,267',
+'71,70,70,235,114,217,42,267,217,147,160,147,96,147,160,160,96,181,160',
+'160,160,160,160,160,237,160,160,217,180,180,214,159,160,42,147,45,45',
+'42,70,160,159,159,159,159,159,264,159,264,159,147,147,159,159,159,159',
+'275,147,179,122,275,70,179,275,122,213,180,44,178,180,159,44,178,45',
+'159,159,45,210,159,159,159,159,159,159,124,159,159,124,180,158,97,304',
+'159,304,241,45,92,255,257,159,158,158,158,97,158,97,158,97,259,158,158',
+'158,158,221,221,260,95,90,48,48,221,7,7,7,7,48,263,158,97,95,209,95',
+'158,95,265,158,158,158,158,158,158,266,158,158,127,202,157,270,271,158',
+'221,272,273,200,95,48,158,157,157,157,277,157,132,157,62,60,157,157',
+'157,157,146,196,195,289,193,101,43,191,298,299,37,171,36,146,157,146',
+'307,146,308,310,311,315,157,157,157,157,157,157,316,157,157,35,322,323',
+'325,10,157,10,146,170,5,1,340,157,10,10,10,10,10,345,10,347,10,146,146',
+'10,10,10,10,156,146,349,,,,,,,,,,156,156,10,156,,156,10,10,156,,10,10',
+'10,10,10,10,,10,10,,,,,,10,,156,,,,,10,155,,156,156,156,156,156,156',
+',156,156,,155,155,,155,156,155,,,155,,,156,154,,,,,,,,,,,,154,154,155',
+'154,,154,,,154,,155,155,155,155,155,155,,155,155,,,,,,155,,154,,,,,155',
+'153,,154,154,154,154,154,154,,154,154,,153,153,,153,154,153,,,153,,',
+'154,152,,,,,,,,,,,,,152,153,152,,152,,,,,153,153,153,153,153,153,,153',
+'153,,,,,,153,,152,,,,,153,,,152,152,152,152,152,152,,152,152,,,,,303',
+'152,,,,,,,152,303,303,303,303,303,,303,,303,,,303,303,303,303,151,,',
+',,,,,,,,,,151,303,151,,151,303,303,,,303,303,303,303,303,303,,303,303',
+',,,,,303,,151,,,,150,303,,,151,151,151,151,151,151,,151,151,150,,150',
+',150,151,,,,,,149,151,,,,,,,,,,,,149,150,149,,149,,,,,150,150,150,150',
+',,,150,150,,,,,,150,,149,,,,,150,,,149,149,149,149,,,,149,149,,,,,166',
+'149,,,,,,,149,166,166,166,166,166,166,166,,166,,,166,166,166,166,,,',
+',,,,,,,,,,,166,,,,166,166,,,166,166,166,166,166,166,,166,166,,,,,297',
+'166,,,,,,,166,297,297,297,297,297,,297,,297,,,297,297,297,297,,,,,,',
+',,,,,,,,297,,,,297,297,,,297,297,297,297,297,297,,297,297,,,,,296,297',
+',,,,,,297,296,296,296,296,296,,296,,296,,,296,296,296,296,145,,,,,,',
+',,,,,,145,296,145,,145,296,296,,,296,296,296,296,296,296,,296,296,,',
+',,,296,,145,,,,,296,,,,,145,145,,,,145,145,,,,,288,145,,,,,,,145,288',
+'288,288,288,288,,288,,288,,,288,288,288,288,144,,,,,,,,,,,,,144,288',
+'144,,144,288,288,,,288,288,288,288,288,288,,288,288,,,,,,288,,144,,',
+',,288,,,,,144,144,,,,144,144,,,,,286,144,,,,,,,144,286,286,286,286,286',
+',286,,286,,,286,286,286,286,,,,,,,,,,,,,,,286,,,,286,286,,,286,286,286',
+'286,286,286,,286,286,,,,,190,286,,,,,,,286,190,190,190,190,190,,190',
+',190,,,190,190,190,190,,,,,,,,,,,,,,,190,,,,190,190,,,190,190,190,190',
+'190,190,,190,190,,,,,282,190,,,,,,,190,282,282,282,282,282,,282,,282',
+',,282,282,282,282,,,,,,,,,,,,,,,282,,,,282,282,,,282,282,282,282,282',
+'282,,282,282,,,,,128,282,,,,,,,282,128,128,128,128,128,,128,,128,,,128',
+'128,128,128,,,,,,,,,,,,,,,128,,,,128,128,,,128,128,128,128,128,128,',
+'128,128,,,,,121,128,,,,,,,128,121,121,121,121,121,,121,,121,,,121,121',
+'121,121,,,,,,,,,,,,,,,121,,,,121,121,,,121,121,121,121,121,121,,121',
+'121,,,,,108,121,108,,,,,,121,108,108,108,108,108,,108,,108,,,108,108',
+'108,108,,,,,,,,,,,,,,,108,,,,108,108,,,108,108,108,108,108,108,,108',
+'108,,,,,107,108,107,,,,,,108,107,107,107,107,107,,107,,107,,,107,107',
+'107,107,,,,,,,,,,,,,,,107,,,,107,107,,,107,107,107,107,107,107,,107',
+'107,,,,,106,107,106,,,,,,107,106,106,106,106,106,,106,,106,,,106,106',
+'106,106,,,,,,,,,,,,,,,106,,,,106,106,,,106,106,106,106,106,106,,106',
+'106,,,,,104,106,104,,,,,,106,104,104,104,104,104,,104,,104,,,104,104',
+'104,104,,,,,,,,,,,,,,,104,,,,104,104,,,104,104,104,104,104,104,,104',
+'104,,,,,98,104,,,,,,,104,98,98,98,98,98,,98,,98,,98,98,98,98,98,,,,',
+',,,,,89,89,,,89,98,89,,,98,98,,,98,98,98,98,98,98,,98,98,89,,,,,98,89',
+',89,,89,89,98,89,89,89,,89,89,,,,89,89,208,208,89,,208,89,208,,,,,,',
+'89,,,,,,89,,89,208,,,,,,208,208,208,208,208,208,208,208,208,208,,208',
+'208,,,,208,208,208,208,208,278,278,208,,278,,278,278,,,208,,,,,208,208',
+',,,,,278,,,,,,278,,278,,278,278,,278,278,278,,278,278,278,,,278,278',
+'268,268,278,,268,278,268,268,,,,,,278,,,,,,278,,,268,,,,,,268,,268,',
+'268,268,,268,268,268,,268,268,,,,268,268,72,72,268,,72,268,72,,,,,,',
+'268,,,,,,268,,,72,,,,,,72,,72,,72,72,,72,72,72,,72,72,,,,72,72,73,73',
+'72,,73,72,73,,,,,,,72,,,,,,72,,,73,,,,,,73,,73,,73,73,,73,73,73,,73',
+'73,,,,73,73,74,74,73,,74,73,74,,,,,,,73,,,,,,73,,,74,,,,,,74,,74,,74',
+'74,,74,74,74,,74,74,,,,74,74,75,75,74,,75,74,75,,,,,,,74,,,,,,74,,,75',
+',,,,,75,,75,,75,75,,75,75,75,,75,75,,,,75,75,76,76,75,,76,75,76,,,,',
+',,75,,,,,,75,,,76,,,,,,76,,76,,76,76,,76,76,76,,76,76,,,,76,76,77,77',
+'76,,77,76,77,,,,,,,76,,,,,,76,,,77,,,,,,77,,77,,77,77,,77,77,77,,77',
+'77,,,,77,77,78,78,77,,78,77,78,,,,,,,77,,,,,,77,,,78,,,,,,78,,78,,78',
+'78,,78,78,78,,78,78,,,,78,78,79,79,78,,79,78,79,,,,,,,78,,,,,,78,,,79',
+',,,,,79,,79,,79,79,,79,79,79,,79,79,,,,79,79,80,80,79,,80,79,80,,,,',
+',,79,,,,,,79,,,80,,,,,,80,,80,,80,80,,80,80,80,,80,80,,,,80,80,81,81',
+'80,,81,80,81,,,,,,,80,,,,,,80,,,81,,,,,,81,,81,,81,81,,81,81,81,,81',
+'81,,,,81,81,82,82,81,,82,81,82,,,,,,,81,,,,,,81,,,82,,,,,,82,,82,,82',
+'82,,82,82,82,,82,82,,,,82,82,83,83,82,,83,82,83,,,,,,,82,,,,,,82,,,83',
+',,,,,83,,83,,83,83,,83,83,83,,83,83,,,,83,83,84,84,83,,84,83,84,,,,',
+',,83,,,,,,83,,,84,,,,,,84,,84,,84,84,,84,84,84,,84,84,,,,84,84,85,85',
+'84,,85,84,85,,,,,,,84,,,,,,84,,,85,,,,,,85,,85,,85,85,,85,85,85,,85',
+'85,,,,85,85,86,86,85,,86,85,86,,,,,,,85,,,,,,85,,,86,,,,,,86,,86,,86',
+'86,,86,86,86,,86,86,,,,86,86,87,87,86,,87,86,87,,,,,,,86,,,,,,86,,,87',
+',,,,,87,,87,,87,87,,87,87,87,,87,87,,,,87,87,88,88,87,,88,87,88,,,,',
+',,87,,,,,,87,,,88,,,,,,88,,88,,88,88,,88,88,88,,88,88,,,,88,88,68,68',
+'88,,68,88,68,,,,,,,88,,,,,,88,,,68,,,,,,68,,68,,68,68,,68,68,68,,68',
+'68,,,,68,68,261,261,68,,261,68,261,,,,,,,68,,,,,,68,,,261,,,,,,261,',
+'261,,261,261,,261,261,261,,261,261,,,,261,261,91,91,261,,91,261,91,',
+',,,,,261,,,,,,261,,,91,,,,,,91,91,91,91,91,91,91,91,91,91,,91,91,,,',
+'91,91,91,91,91,254,254,91,,254,,254,,,,91,,,,,91,91,,,,,,254,,,,,,254',
+',254,,254,254,,254,254,254,,254,254,,,,254,254,93,93,254,,93,254,93',
+',,,,,,254,,,,,,254,,,93,,,,,,93,,93,,93,93,,93,93,93,,93,93,,,,93,93',
+'94,94,93,,94,93,94,,,,,,,93,,,,,,93,,,94,,,,,,94,,94,,94,94,,94,94,94',
+',94,94,,,,94,94,240,240,94,,240,94,240,,,,,,,94,,,,,,94,,,240,,,,,,240',
+',240,,240,240,,240,240,240,,240,240,,,,240,240,239,239,240,,239,240',
+'239,,,,,,,240,,,,,,240,,,239,,,,,,239,,239,,239,239,,239,239,239,,239',
+'239,,,,239,239,236,236,239,,236,239,236,,,,,,,239,,,,,,239,,,236,,,',
+',,236,,236,,236,236,,236,236,236,,236,236,,,,236,236,67,67,236,,67,236',
+'67,,,,,,,236,,,,,,236,,,67,,,,,,67,,67,,67,67,,67,67,67,,67,67,67,,',
+'67,67,99,99,67,,99,67,99,,,,,,,67,,,,,,67,,,99,99,,,,,99,,99,,99,99',
+',99,99,99,,99,99,,,,99,99,230,230,99,,230,99,230,,,,,,,99,,,,,,99,,',
+'230,,,,,,230,,230,,230,230,,230,230,230,,230,230,,,,230,230,229,229',
+'230,,229,230,229,,,,,,,230,,,,,,230,,,229,,,,,,229,,229,,229,229,,229',
+'229,229,,229,229,,,,229,229,103,103,229,,103,229,103,,,,,,,229,,,,,',
+'229,,,103,103,,,,,103,,103,,103,103,,103,103,103,,103,103,,,,103,103',
+'66,66,103,,66,103,66,,,,,,,103,,,,,,103,,,66,,,,,,66,,66,,66,66,,66',
+'66,66,,66,66,66,,,66,66,65,65,66,,65,66,65,,,,,,,66,,,,,,66,,,65,,,',
+',,65,,65,,65,65,,65,65,65,,65,65,65,,,65,65,64,64,65,,64,65,64,,,,,',
+',65,,,,,,65,,,64,,,,,,64,,64,,64,64,,64,64,64,,64,64,64,,,64,64,63,63',
+'64,,63,64,63,,,,,,,64,,,,,,64,,,63,,,,,,63,,63,,63,63,,63,63,63,,63',
+'63,63,,,63,63,109,109,63,,109,63,109,,,,,,,63,,,,,,63,,,109,,,,,,109',
+',109,,109,109,,109,109,109,,109,109,,,,109,109,227,227,109,,227,109',
+'227,,,,,,,109,,,,,,109,,,227,,,,,,227,,227,,227,227,,227,227,227,,227',
+'227,,,,227,227,222,222,227,,222,227,222,,,,,,,227,,,,,,227,,,222,,,',
+',,222,,222,,222,222,,222,222,222,,222,222,,,,222,222,,,222,218,218,222',
+',218,218,218,,,,222,,,,,,222,,,,,,218,,,,,,218,,218,,218,218,,218,218',
+'218,,218,218,,,,218,218,279,279,218,,279,218,279,279,,,,,,218,,,,,,218',
+',,279,,,,,,279,,279,,279,279,,279,279,279,,279,279,279,,,279,279,69',
+'69,279,,69,279,69,,,,,,,279,,,,,,279,,,69,,,,,,69,,69,,69,69,,69,69',
+'69,,69,69,,,,69,69,207,207,69,,207,69,207,,,,,,,69,,,,,,69,,,207,,,',
+',,207,,207,,207,207,,207,207,207,,207,207,,,,207,207,206,206,207,,206',
+'207,206,206,,,,,,207,,,,,,207,,,206,,,,,,206,,206,,206,206,,206,206',
+'206,,206,206,206,,,206,206,61,61,206,,61,206,61,,,,,,,206,,,,,,206,',
+',61,,,,,,61,,61,,61,61,,61,61,61,,61,61,61,,,61,61,164,164,61,,164,61',
+'164,,,,,,,61,,,,,,61,,,164,,,,,,164,,164,,164,164,,164,164,164,,164',
+'164,,,,164,164,198,198,164,,198,164,198,198,,,,,,164,,,,,,164,,,198',
+',,,,,198,,198,,198,198,,198,198,198,,198,198,198,,,198,198,52,52,198',
+',52,198,52,,,,,,,198,,,,,,198,,,52,,,,,,52,,52,,52,52,,52,52,52,,52',
+'52,,,,52,52,169,169,52,,169,52,169,,,,,,,52,,,,,,52,,,169,,,,,,169,',
+'169,,169,169,,169,169,169,,169,169,,,,169,169,,,169,47,47,169,,47,47',
+'47,,,,169,,,,,,169,,,,,,47,,,,,,47,,47,,47,47,,47,47,47,,47,47,,,,47',
+'47,290,290,47,,290,47,290,,,,,,,47,,,,,,47,,,290,,,,,,290,,290,,290',
+'290,,290,290,290,,290,290,,,,290,290,168,168,290,,168,290,168,,,,,,',
+'290,,,,,,290,,,168,,,,,,168,,168,,168,168,,168,168,168,,168,168,,,,168',
+'168,167,167,168,,167,168,167,,,,,,,168,,,,,,168,,,167,,,,,,167,,167',
+',167,167,,167,167,167,,167,167,,,,167,167,41,41,167,,41,167,41,,,,,',
+',167,,,,,,167,,,41,,,,,,41,,41,,41,41,,41,41,41,,41,41,,,,41,41,40,40',
+'41,,40,41,40,,,,,,,41,,,,,,41,,,40,,,,,,40,,40,,40,40,,40,40,40,,40',
+'40,,,,40,40,39,39,40,,39,40,39,,,,,,,40,,,,,,40,,,39,,,,,,39,,39,,39',
+'39,,39,39,39,,39,39,,,,39,39,38,38,39,,38,39,38,,,,,,,39,,,,,,39,,,38',
+',,,,,38,,38,,38,38,,38,38,38,,38,38,,,,38,38,306,306,38,,306,38,306',
+',,,,,,38,,,,,,38,,,306,,,,,,306,,306,,306,306,,306,306,306,,306,306',
+',,,306,306,318,318,306,,318,306,318,318,,,,,,306,,,,,,306,,,318,,,,',
+',318,,318,,318,318,,318,318,318,,318,318,318,,,318,318,13,13,318,,13',
+'318,13,,,,,,,318,,,,,,318,,,13,,,,,,13,,13,,13,13,,13,13,13,,13,13,',
+',,13,13,12,12,13,,12,13,12,,,,,,,13,,,,,,13,,,12,,,,,,12,,12,,12,12',
+',12,12,12,,12,12,,,,12,12,11,11,12,,11,12,11,,,,,,,12,,,,,,12,,,11,',
+',,,,11,,11,,11,11,,11,11,11,,11,11,,,,11,11,334,334,11,,334,11,334,334',
+',,,,,11,,,,,,11,,,334,,,,,,334,,334,,334,334,,334,334,334,,334,334,334',
+',,334,334,336,336,334,,336,334,336,336,,,,,,334,,,,,,334,,,336,,,,,',
+'336,,336,,336,336,,336,336,336,,336,336,336,,,336,336,4,4,336,,4,336',
+'4,,,,,,,336,,,,,,336,,,4,,,,,,4,,4,,4,4,,4,4,4,4,4,4,4,,,4,4,337,337',
+'4,,337,4,337,337,,,,,,4,,,,,,4,,,337,,,,,,337,,337,,337,337,,337,337',
+'337,,337,337,337,,,337,337,0,0,337,,0,337,0,,,,,,,337,,,,,,337,,,0,',
+',,,,0,,0,,0,0,,0,0,0,,0,0,0,,,0,0,205,205,0,,205,0,205,205,,,,,,0,,',
+',,,0,,,205,,,,,,205,,205,,205,205,,205,205,205,,205,205,205,,,205,205',
+',,205,,,205,,,,233,233,233,233,205,233,233,233,233,233,205,233,233,',
+',,,,233,233,233,189,189,189,189,,189,189,189,189,189,,189,189,,,233',
+'233,,189,189,189,238,238,238,238,,238,238,238,238,238,,238,238,,,189',
+'189,,238,238,238,,,,,,,,,,,,,,,,238,238' ]
+ racc_action_check = arr = ::Array.new(4874, nil)
idx = 0
clist.each do |str|
str.split(',', -1).each do |i|
@@ -353,225 +353,228 @@ clist = [
end
racc_action_pointer = [
- 4621, 340, nil, nil, 4529, 327, nil, 219, nil, nil,
- 247, 4391, 4345, 4299, nil, nil, nil, nil, nil, nil,
+ 4691, 297, nil, nil, 4599, 284, nil, 145, nil, nil,
+ 285, 4461, 4415, 4369, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 309, 115, 288, 4161, 4115,
- 4069, 4023, -6, 262, 95, 208, nil, 3839, 135, nil,
- nil, nil, 3744, nil, nil, nil, nil, nil, nil, nil,
- 285, 3606, 267, 3189, 3143, 3097, 3051, 2775, 2358, 3468,
- 5, 16, 1576, 1622, 1668, 1714, 1760, 1806, 1852, 1898,
- 1944, 1990, 2036, 2082, 2128, 2174, 2220, 4667, 2312, 1386,
- 154, 2450, 165, 2545, 2591, 213, 43, 112, 1327, 2821,
- nil, nil, nil, 47, 149, -31, 3005, 1275, nil, 1214,
- 1153, 1092, 3235, 286, nil, nil, nil, 290, -8, nil,
- nil, nil, nil, nil, 1031, 19, nil, 99, nil, nil,
- 98, 979, nil, 219, nil, 215, nil, nil, nil, nil,
- nil, 23, 156, nil, nil, nil, nil, 771, 701, 427,
- 212, 122, 527, 503, 475, 375, 399, 347, 323, 271,
- 186, 142, 98, 46, -6, nil, nil, 2266, nil, 579,
- 3977, 3931, 3790, 268, 281, nil, nil, 5, nil, -10,
- 25, 54, 48, 187, 29, 28, nil, nil, nil, nil,
- nil, nil, 4694, 875, 242, nil, 264, nil, 255, 71,
- 220, nil, nil, 3698, nil, 137, nil, 117, 2, nil,
- 3652, 3560, 3514, 1435, 214, 280, nil, 0, 330, 309,
- 68, nil, 219, 3376, nil, nil, 83, 3327, nil, nil,
- nil, nil, 3281, nil, 2959, 2913, 111, nil, 4715, nil,
- 36, 2867, 72, 4736, 2729, 2683, 124, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, 2637,
- 142, nil, nil, 2499, 171, nil, 117, 153, 2404, nil,
- 197, 26, 211, 194, 12, 1530, nil, 197, 232, 240,
- 243, nil, 49, nil, 247, 1484, 3422, nil, nil, 927,
- nil, nil, nil, 823, nil, 753, 279, 3885, 286, nil,
- nil, nil, nil, 683, 631, 300, 28, 302, nil, nil,
- nil, nil, 451, 145, nil, 4207, -2, 287, nil, 311,
- 312, nil, nil, nil, 313, 323, nil, 4253, nil, nil,
- nil, 309, 326, nil, 327, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 4437, nil, 4483, 4575, nil, nil,
- 334, nil, nil, nil, nil, 338, nil, 339, nil, 341,
+ nil, nil, nil, nil, nil, 263, 200, 242, 4231, 4185,
+ 4139, 4093, 85, 219, 118, 120, nil, 3909, 202, nil,
+ nil, nil, 3814, nil, nil, nil, nil, nil, nil, nil,
+ 251, 3676, 238, 3259, 3213, 3167, 3121, 2891, 2474, 3538,
+ 86, 26, 1692, 1738, 1784, 1830, 1876, 1922, 1968, 2014,
+ 2060, 2106, 2152, 2198, 2244, 2290, 2336, 2382, 2428, 1505,
+ 164, 2566, 174, 2661, 2707, 196, 64, 170, 1468, 2937,
+ nil, 253, -28, 3075, 1409, nil, 1350, 1291, 1232, 3305,
+ 21, nil, nil, nil, 67, -11, nil, nil, nil, nil,
+ nil, 1173, 138, nil, 161, nil, nil, 219, 1114, nil,
+ 26, nil, 236, nil, nil, nil, nil, nil, -5, 13,
+ nil, nil, nil, nil, 878, 795, 250, 77, 18, 594,
+ 570, 528, 445, 421, 377, 353, 309, 226, 169, 112,
+ 53, -6, nil, nil, 3722, nil, 653, 4047, 4001, 3860,
+ 255, 255, nil, nil, 7, nil, -7, 22, 119, 109,
+ 113, 53, 24, nil, nil, nil, nil, nil, nil, 4785,
+ 996, 218, nil, 238, nil, 246, 189, nil, 3768, nil,
+ 227, nil, 216, -9, nil, 4737, 3630, 3584, 1551, 176,
+ 127, nil, 27, 143, 109, 24, nil, 53, 3446, nil,
+ nil, 197, 3397, nil, nil, nil, nil, 3351, nil, 3029,
+ 2983, 2, nil, 4764, nil, 81, 2845, 102, 4806, 2799,
+ 2753, 168, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, 2615, 158, nil, 175, nil, 126,
+ 167, 2520, nil, 203, 101, 211, 196, 74, 1646, nil,
+ 193, 222, 228, 230, nil, 107, nil, 234, 1600, 3492,
+ nil, nil, 1055, nil, nil, nil, 937, nil, 854, 250,
+ 3955, -4, nil, nil, nil, nil, 771, 712, 255, 197,
+ nil, nil, nil, 504, 146, nil, 4277, 264, 243, nil,
+ 267, 268, nil, nil, nil, 268, 275, nil, 4323, nil,
+ nil, nil, 263, 280, nil, 281, nil, nil, nil, nil,
+ nil, nil, nil, nil, 4507, nil, 4553, 4645, nil, nil,
+ 289, nil, nil, nil, nil, 296, nil, 298, nil, 308,
nil, nil, nil, nil, nil ]
racc_action_default = [
- -209, -210, -1, -2, -3, -4, -7, -9, -10, -15,
- -109, -210, -210, -210, -43, -44, -45, -46, -47, -48,
+ -203, -204, -1, -2, -3, -4, -7, -9, -10, -15,
+ -103, -204, -204, -204, -43, -44, -45, -46, -47, -48,
-49, -50, -51, -52, -53, -54, -55, -56, -57, -58,
- -59, -60, -61, -62, -63, -68, -69, -73, -210, -210,
- -210, -210, -210, -119, -210, -210, -164, -210, -210, -174,
- -175, -176, -210, -178, -185, -186, -187, -188, -189, -190,
- -210, -210, -6, -210, -210, -210, -210, -210, -210, -210,
- -210, -210, -210, -210, -210, -210, -210, -210, -210, -210,
- -210, -210, -210, -210, -210, -210, -210, -210, -210, -210,
- -210, -127, -122, -209, -209, -27, -210, -34, -210, -210,
- -70, -75, -76, -209, -210, -210, -210, -210, -86, -210,
- -210, -210, -210, -209, -153, -154, -120, -209, -209, -145,
- -147, -148, -149, -150, -41, -210, -167, -210, -170, -171,
- -210, -182, -177, -210, 365, -5, -8, -11, -12, -13,
- -14, -210, -17, -18, -162, -163, -19, -20, -21, -22,
- -23, -24, -25, -26, -28, -29, -30, -31, -32, -33,
- -35, -36, -37, -38, -210, -39, -104, -210, -74, -210,
- -202, -208, -196, -193, -191, -117, -128, -185, -131, -189,
- -210, -199, -197, -205, -187, -188, -195, -200, -201, -203,
- -204, -206, -127, -126, -210, -125, -210, -40, -191, -65,
- -210, -80, -81, -210, -84, -191, -158, -161, -210, -72,
- -210, -210, -210, -127, -193, -209, -155, -210, -210, -210,
- -210, -151, -210, -210, -165, -168, -210, -210, -179, -180,
- -181, -183, -210, -16, -210, -210, -191, -106, -127, -116,
- -210, -194, -210, -192, -210, -210, -191, -130, -132, -196,
- -197, -198, -199, -202, -205, -207, -208, -123, -124, -192,
- -210, -67, -77, -210, -210, -83, -210, -192, -210, -71,
- -210, -89, -210, -95, -210, -210, -99, -193, -191, -210,
- -210, -139, -210, -156, -191, -210, -210, -146, -152, -42,
- -166, -169, -172, -173, -184, -108, -210, -192, -191, -112,
- -118, -113, -129, -133, -134, -210, -64, -210, -79, -82,
- -85, -159, -160, -89, -88, -210, -210, -95, -94, -210,
- -210, -103, -98, -100, -210, -210, -114, -210, -140, -141,
- -142, -210, -210, -136, -210, -144, -105, -107, -115, -121,
- -66, -78, -87, -90, -210, -93, -210, -210, -110, -111,
- -210, -138, -157, -135, -143, -210, -92, -210, -97, -210,
- -102, -137, -91, -96, -101 ]
+ -59, -60, -61, -62, -63, -68, -69, -73, -204, -204,
+ -204, -204, -204, -113, -204, -204, -158, -204, -204, -168,
+ -169, -170, -204, -172, -179, -180, -181, -182, -183, -184,
+ -204, -204, -6, -204, -204, -204, -204, -204, -204, -204,
+ -204, -204, -204, -204, -204, -204, -204, -204, -204, -204,
+ -204, -204, -204, -204, -204, -204, -204, -204, -204, -204,
+ -204, -121, -116, -203, -203, -27, -204, -34, -204, -204,
+ -70, -204, -204, -204, -204, -80, -204, -204, -204, -204,
+ -203, -147, -148, -114, -203, -203, -139, -141, -142, -143,
+ -144, -41, -204, -161, -204, -164, -165, -204, -176, -171,
+ -204, 355, -5, -8, -11, -12, -13, -14, -204, -17,
+ -18, -156, -157, -19, -20, -21, -22, -23, -24, -25,
+ -26, -28, -29, -30, -31, -32, -33, -35, -36, -37,
+ -38, -204, -39, -98, -204, -74, -204, -196, -202, -190,
+ -187, -185, -111, -122, -179, -125, -183, -204, -193, -191,
+ -199, -181, -182, -189, -194, -195, -197, -198, -200, -121,
+ -120, -204, -119, -204, -40, -185, -65, -75, -204, -78,
+ -185, -152, -155, -204, -72, -204, -204, -204, -121, -187,
+ -203, -149, -204, -204, -204, -204, -145, -204, -204, -159,
+ -162, -204, -204, -173, -174, -175, -177, -204, -16, -204,
+ -204, -185, -100, -121, -110, -204, -188, -204, -186, -204,
+ -204, -185, -124, -126, -190, -191, -192, -193, -196, -199,
+ -201, -202, -117, -118, -186, -204, -67, -204, -77, -204,
+ -186, -204, -71, -204, -83, -204, -89, -204, -204, -93,
+ -187, -185, -204, -204, -133, -204, -150, -185, -204, -204,
+ -140, -146, -42, -160, -163, -166, -167, -178, -102, -204,
+ -186, -185, -106, -112, -107, -123, -127, -128, -204, -64,
+ -76, -79, -153, -154, -83, -82, -204, -204, -89, -88,
+ -204, -204, -97, -92, -94, -204, -204, -108, -204, -134,
+ -135, -136, -204, -204, -130, -204, -138, -99, -101, -109,
+ -115, -66, -81, -84, -204, -87, -204, -204, -104, -105,
+ -204, -132, -151, -129, -137, -204, -86, -204, -91, -204,
+ -96, -131, -85, -90, -95 ]
racc_goto_table = [
- 2, 117, 100, 95, 97, 98, 3, 132, 129, 166,
- 174, 240, 130, 205, 314, 318, 123, 121, 215, 173,
- 1, 276, 218, 320, 287, 62, 288, 242, 194, 196,
- 107, 109, 110, 111, 145, 145, 125, 143, 146, 124,
- 214, 144, 144, 236, 131, 137, 138, 139, 140, 275,
- 300, 260, 279, 238, 343, 302, 342, 141, 266, 345,
- 124, 142, 262, 200, 147, 148, 149, 150, 151, 152,
- 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
- 163, 164, 135, 169, 323, 193, 193, 237, 198, 296,
- 280, 124, 328, 219, 203, 208, 311, 127, 124, 305,
- 165, 136, 231, 232, 169, 230, nil, nil, nil, 201,
- nil, 246, nil, nil, nil, 324, nil, nil, nil, 216,
- nil, nil, nil, 216, 221, 284, nil, nil, nil, nil,
- nil, 325, 278, nil, nil, nil, nil, 331, 117, nil,
- nil, 277, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 338, nil, nil, 123, 121, nil, 298, nil, 164,
- nil, nil, 107, 109, 110, 261, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 292, 294, nil, nil,
- 130, 123, 121, 123, 121, nil, nil, nil, nil, nil,
- nil, nil, nil, 264, 124, 169, nil, nil, nil, nil,
- 270, 272, nil, nil, nil, 289, nil, 337, nil, 293,
- nil, 281, nil, nil, 131, nil, 289, 295, nil, nil,
- nil, nil, nil, 169, nil, nil, 303, 304, nil, 329,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 289, nil, nil, nil, nil, nil, nil, nil, nil,
- 312, nil, nil, 307, nil, nil, nil, 124, nil, nil,
- nil, nil, 340, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 332, 334, nil, nil, 164,
+ 2, 3, 100, 95, 97, 98, 114, 163, 129, 126,
+ 170, 171, 127, 200, 118, 305, 309, 120, 235, 210,
+ 280, 311, 281, 213, 237, 231, 269, 293, 209, 233,
+ 104, 106, 107, 108, 142, 142, 62, 140, 143, 121,
+ 191, 193, 141, 141, 128, 268, 295, 333, 255, 134,
+ 135, 136, 137, 259, 197, 332, 273, 272, 335, 319,
+ 121, 139, 214, 162, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
+ 160, 161, 232, 166, 289, 190, 190, 314, 302, 122,
+ 124, 121, 133, 132, 298, 121, 1, 226, 227, 225,
+ nil, 166, nil, nil, nil, nil, nil, nil, nil, 241,
+ 138, 211, nil, nil, nil, 211, 216, nil, 315, nil,
+ nil, nil, nil, 277, 316, nil, nil, 270, 271, nil,
+ 322, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 114, 195, nil, nil, 329, 203, nil, nil, nil, 118,
+ nil, nil, 120, 291, nil, nil, 161, nil, nil, 104,
+ 106, 107, 256, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 107, nil, nil,
+ nil, nil, 285, 287, 118, 127, 118, 120, nil, 120,
+ nil, nil, nil, nil, nil, nil, nil, nil, 257, 121,
+ 166, nil, nil, nil, nil, 263, 265, nil, 328, nil,
+ 282, 274, nil, nil, 286, nil, nil, nil, nil, 128,
+ nil, 282, 288, nil, nil, nil, nil, nil, 166, nil,
+ nil, 296, 297, nil, nil, nil, nil, 320, nil, nil,
+ nil, nil, nil, nil, nil, nil, 282, nil, nil, nil,
+ nil, nil, nil, 303, nil, nil, nil, nil, nil, nil,
+ 121, nil, nil, nil, nil, 331, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 323, 325,
+ nil, nil, 161, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 104, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 350, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 340, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, 355, nil, 357, 359 ]
+ nil, nil, nil, nil, 345, nil, 347, 349 ]
racc_goto_check = [
- 2, 65, 37, 9, 9, 9, 3, 78, 74, 52,
- 57, 56, 31, 45, 47, 48, 30, 35, 66, 55,
- 1, 50, 66, 51, 71, 5, 71, 36, 61, 61,
- 9, 9, 9, 9, 31, 31, 11, 12, 12, 9,
- 55, 30, 30, 53, 9, 7, 7, 7, 7, 49,
- 58, 36, 56, 59, 46, 62, 47, 11, 36, 48,
- 9, 9, 44, 43, 9, 9, 9, 9, 9, 9,
+ 2, 3, 37, 9, 9, 9, 62, 49, 75, 71,
+ 52, 54, 31, 42, 35, 44, 45, 30, 53, 63,
+ 68, 48, 68, 63, 36, 50, 47, 55, 52, 56,
+ 9, 9, 9, 9, 31, 31, 5, 12, 12, 9,
+ 58, 58, 30, 30, 9, 46, 59, 43, 36, 7,
+ 7, 7, 7, 36, 41, 44, 64, 53, 45, 65,
+ 9, 9, 67, 13, 9, 9, 9, 9, 9, 9,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 5, 9, 50, 9, 9, 52, 11, 36,
- 67, 9, 68, 70, 42, 11, 72, 73, 9, 36,
- 13, 6, 79, 80, 9, 82, nil, nil, nil, 3,
- nil, 57, nil, nil, nil, 56, nil, nil, nil, 3,
- nil, nil, nil, 3, 3, 45, nil, nil, nil, nil,
- nil, 36, 57, nil, nil, nil, nil, 36, 65, nil,
- nil, 55, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 36, nil, nil, 30, 35, nil, 57, nil, 9,
- nil, nil, 9, 9, 9, 37, nil, nil, nil, nil,
+ 9, 9, 49, 9, 36, 9, 9, 47, 69, 11,
+ 70, 9, 6, 5, 36, 9, 1, 76, 77, 79,
+ nil, 9, nil, nil, nil, nil, nil, nil, nil, 54,
+ 11, 3, nil, nil, nil, 3, 3, nil, 53, nil,
+ nil, nil, nil, 42, 36, nil, nil, 52, 54, nil,
+ 36, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ 62, 11, nil, nil, 36, 11, nil, nil, nil, 35,
+ nil, nil, 30, 54, nil, nil, 9, nil, nil, 9,
+ 9, 9, 37, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, 74, 78, nil, nil,
- 31, 30, 35, 30, 35, nil, nil, nil, nil, nil,
- nil, nil, nil, 2, 9, 9, nil, nil, nil, nil,
- 2, 2, nil, nil, nil, 9, nil, 52, nil, 9,
- nil, 3, nil, nil, 9, nil, 9, 9, nil, nil,
- nil, nil, nil, 9, nil, nil, 9, 9, nil, 65,
+ nil, nil, 71, 75, 35, 31, 35, 30, nil, 30,
+ nil, nil, nil, nil, nil, nil, nil, nil, 2, 9,
+ 9, nil, nil, nil, nil, 2, 2, nil, 49, nil,
+ 9, 3, nil, nil, 9, nil, nil, nil, nil, 9,
+ nil, 9, 9, nil, nil, nil, nil, nil, 9, nil,
+ nil, 9, 9, nil, nil, nil, nil, 62, nil, nil,
+ nil, nil, nil, nil, nil, nil, 9, nil, nil, nil,
+ nil, nil, nil, 9, nil, nil, nil, nil, nil, nil,
+ 9, nil, nil, nil, nil, 37, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 2, 2,
+ nil, nil, 9, nil, nil, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 9, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, 9, nil, nil, nil, nil, nil, nil, nil, nil,
- 9, nil, nil, 2, nil, nil, nil, 9, nil, nil,
- nil, nil, 37, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, 2, 2, nil, nil, 9,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 9, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- nil, nil, nil, nil, nil, nil, nil, 2, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, 2, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, 2, nil, 2, 2 ]
racc_goto_pointer = [
- nil, 20, 0, 6, nil, 21, 38, -19, nil, -8,
- nil, -11, -33, 11, nil, nil, nil, nil, nil, nil,
+ nil, 96, 0, 1, nil, 32, 29, -15, nil, -8,
+ nil, 42, -33, -26, nil, nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
- -29, -36, nil, nil, nil, -28, -147, -34, nil, nil,
- nil, nil, -10, -40, -138, -92, -261, -257, -258, -163,
- -191, -251, -80, -124, nil, -72, -162, -81, -191, -116,
- nil, -65, -188, nil, nil, -43, -95, -125, -190, nil,
- -25, -196, -171, 49, -40, nil, nil, nil, -45, -31,
- -30, nil, -28 ]
+ -28, -36, nil, nil, nil, -31, -147, -34, nil, nil,
+ nil, -47, -89, -259, -249, -250, -162, -181, -246, -82,
+ -139, nil, -81, -152, -80, -209, -137, nil, -53, -192,
+ nil, nil, -38, -91, -154, -216, nil, -53, -195, -172,
+ 42, -39, nil, nil, nil, -44, -33, -32, nil, -31 ]
racc_goto_default = [
- nil, nil, nil, 195, 4, 5, 6, 7, 8, 10,
- 9, 274, nil, nil, 14, 35, 15, 16, 17, 18,
+ nil, nil, nil, 192, 4, 5, 6, 7, 8, 10,
+ 9, 267, nil, nil, 14, 35, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 30, 31, 32, 33, 34, nil, nil, 36, 37,
- 101, 102, 103, nil, nil, nil, 108, nil, nil, nil,
- nil, nil, nil, nil, 41, nil, nil, nil, 175, nil,
- 92, nil, 176, 180, 178, 113, nil, nil, nil, 118,
- nil, 119, 206, nil, nil, 49, 50, 52, nil, nil,
- nil, 133, nil ]
+ 101, nil, nil, 105, nil, nil, nil, nil, nil, nil,
+ nil, 41, nil, nil, nil, 172, nil, 92, nil, 173,
+ 177, 175, 110, nil, nil, nil, 115, nil, 116, 201,
+ nil, nil, 49, 50, 52, nil, nil, nil, 130, nil ]
racc_reduce_table = [
0, 0, :racc_error,
- 1, 78, :_reduce_1,
- 1, 78, :_reduce_none,
- 1, 79, :_reduce_3,
- 1, 81, :_reduce_4,
- 3, 81, :_reduce_5,
- 2, 81, :_reduce_6,
- 1, 82, :_reduce_7,
- 3, 82, :_reduce_8,
- 1, 83, :_reduce_none,
- 1, 84, :_reduce_10,
- 3, 84, :_reduce_11,
- 3, 84, :_reduce_12,
- 3, 84, :_reduce_13,
- 3, 84, :_reduce_14,
+ 1, 77, :_reduce_1,
+ 1, 77, :_reduce_none,
+ 1, 78, :_reduce_3,
+ 1, 80, :_reduce_4,
+ 3, 80, :_reduce_5,
+ 2, 80, :_reduce_6,
+ 1, 81, :_reduce_7,
+ 3, 81, :_reduce_8,
+ 1, 82, :_reduce_none,
+ 1, 83, :_reduce_10,
+ 3, 83, :_reduce_11,
+ 3, 83, :_reduce_12,
+ 3, 83, :_reduce_13,
+ 3, 83, :_reduce_14,
+ 1, 85, :_reduce_none,
+ 4, 85, :_reduce_16,
+ 3, 85, :_reduce_17,
+ 3, 85, :_reduce_18,
+ 3, 85, :_reduce_19,
+ 3, 85, :_reduce_20,
+ 3, 85, :_reduce_21,
+ 3, 85, :_reduce_22,
+ 3, 85, :_reduce_23,
+ 3, 85, :_reduce_24,
+ 3, 85, :_reduce_25,
+ 3, 85, :_reduce_26,
+ 2, 85, :_reduce_27,
+ 3, 85, :_reduce_28,
+ 3, 85, :_reduce_29,
+ 3, 85, :_reduce_30,
+ 3, 85, :_reduce_31,
+ 3, 85, :_reduce_32,
+ 3, 85, :_reduce_33,
+ 2, 85, :_reduce_34,
+ 3, 85, :_reduce_35,
+ 3, 85, :_reduce_36,
+ 3, 85, :_reduce_37,
+ 3, 85, :_reduce_38,
+ 3, 85, :_reduce_39,
+ 3, 85, :_reduce_40,
+ 1, 87, :_reduce_41,
+ 3, 87, :_reduce_42,
1, 86, :_reduce_none,
- 4, 86, :_reduce_16,
- 3, 86, :_reduce_17,
- 3, 86, :_reduce_18,
- 3, 86, :_reduce_19,
- 3, 86, :_reduce_20,
- 3, 86, :_reduce_21,
- 3, 86, :_reduce_22,
- 3, 86, :_reduce_23,
- 3, 86, :_reduce_24,
- 3, 86, :_reduce_25,
- 3, 86, :_reduce_26,
- 2, 86, :_reduce_27,
- 3, 86, :_reduce_28,
- 3, 86, :_reduce_29,
- 3, 86, :_reduce_30,
- 3, 86, :_reduce_31,
- 3, 86, :_reduce_32,
- 3, 86, :_reduce_33,
- 2, 86, :_reduce_34,
- 3, 86, :_reduce_35,
- 3, 86, :_reduce_36,
- 3, 86, :_reduce_37,
- 3, 86, :_reduce_38,
- 3, 86, :_reduce_39,
- 3, 86, :_reduce_40,
- 1, 88, :_reduce_41,
- 3, 88, :_reduce_42,
- 1, 87, :_reduce_none,
- 1, 92, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
+ 1, 91, :_reduce_none,
1, 92, :_reduce_none,
1, 92, :_reduce_none,
1, 92, :_reduce_none,
@@ -580,167 +583,152 @@ racc_reduce_table = [
1, 92, :_reduce_none,
1, 92, :_reduce_none,
1, 92, :_reduce_none,
- 1, 92, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 93, :_reduce_none,
- 1, 108, :_reduce_62,
- 1, 108, :_reduce_63,
- 5, 91, :_reduce_64,
- 3, 91, :_reduce_65,
- 6, 91, :_reduce_66,
- 4, 91, :_reduce_67,
- 1, 91, :_reduce_68,
- 1, 95, :_reduce_69,
- 2, 95, :_reduce_70,
- 4, 115, :_reduce_71,
- 3, 115, :_reduce_72,
- 1, 115, :_reduce_73,
- 3, 116, :_reduce_74,
- 1, 114, :_reduce_none,
- 1, 114, :_reduce_none,
- 3, 117, :_reduce_77,
- 3, 121, :_reduce_78,
- 2, 121, :_reduce_79,
- 1, 120, :_reduce_none,
- 1, 120, :_reduce_none,
- 4, 118, :_reduce_82,
- 3, 118, :_reduce_83,
- 2, 119, :_reduce_84,
- 4, 119, :_reduce_85,
- 2, 98, :_reduce_86,
- 5, 123, :_reduce_87,
- 4, 123, :_reduce_88,
- 0, 124, :_reduce_none,
- 2, 124, :_reduce_90,
- 4, 124, :_reduce_91,
- 3, 124, :_reduce_92,
- 6, 99, :_reduce_93,
- 5, 99, :_reduce_94,
- 0, 125, :_reduce_none,
- 4, 125, :_reduce_96,
- 3, 125, :_reduce_97,
- 5, 97, :_reduce_98,
- 1, 126, :_reduce_99,
- 2, 126, :_reduce_100,
- 5, 127, :_reduce_101,
- 4, 127, :_reduce_102,
- 1, 128, :_reduce_103,
- 1, 90, :_reduce_none,
- 4, 90, :_reduce_105,
- 1, 130, :_reduce_106,
- 3, 130, :_reduce_107,
- 3, 129, :_reduce_108,
- 1, 85, :_reduce_109,
- 6, 85, :_reduce_110,
- 6, 85, :_reduce_111,
- 5, 85, :_reduce_112,
- 5, 85, :_reduce_113,
- 5, 85, :_reduce_114,
- 4, 135, :_reduce_115,
- 1, 136, :_reduce_116,
- 1, 132, :_reduce_117,
- 3, 132, :_reduce_118,
- 1, 131, :_reduce_119,
- 2, 131, :_reduce_120,
- 6, 96, :_reduce_121,
- 2, 96, :_reduce_122,
- 3, 137, :_reduce_123,
- 3, 137, :_reduce_124,
- 1, 138, :_reduce_none,
- 1, 138, :_reduce_none,
- 0, 134, :_reduce_127,
- 1, 134, :_reduce_128,
- 3, 134, :_reduce_129,
- 1, 140, :_reduce_none,
- 1, 140, :_reduce_none,
- 1, 140, :_reduce_none,
- 3, 139, :_reduce_133,
- 3, 139, :_reduce_134,
- 6, 100, :_reduce_135,
- 5, 100, :_reduce_136,
- 7, 101, :_reduce_137,
- 6, 101, :_reduce_138,
- 1, 144, :_reduce_none,
- 2, 144, :_reduce_140,
- 1, 145, :_reduce_none,
- 1, 145, :_reduce_none,
- 6, 102, :_reduce_143,
- 5, 102, :_reduce_144,
- 1, 146, :_reduce_145,
- 3, 146, :_reduce_146,
- 1, 148, :_reduce_147,
- 1, 148, :_reduce_148,
- 1, 148, :_reduce_149,
- 1, 148, :_reduce_none,
- 1, 147, :_reduce_none,
- 2, 147, :_reduce_152,
- 1, 142, :_reduce_153,
- 1, 142, :_reduce_154,
- 1, 143, :_reduce_155,
- 2, 143, :_reduce_156,
- 4, 143, :_reduce_157,
- 1, 122, :_reduce_158,
- 3, 122, :_reduce_159,
- 3, 149, :_reduce_160,
- 1, 149, :_reduce_161,
+ 1, 107, :_reduce_62,
+ 1, 107, :_reduce_63,
+ 5, 90, :_reduce_64,
+ 3, 90, :_reduce_65,
+ 6, 90, :_reduce_66,
+ 4, 90, :_reduce_67,
+ 1, 90, :_reduce_68,
+ 1, 94, :_reduce_69,
+ 2, 94, :_reduce_70,
+ 4, 114, :_reduce_71,
+ 3, 114, :_reduce_72,
+ 1, 114, :_reduce_73,
+ 3, 115, :_reduce_74,
+ 2, 113, :_reduce_75,
+ 3, 117, :_reduce_76,
+ 2, 117, :_reduce_77,
+ 2, 116, :_reduce_78,
+ 4, 116, :_reduce_79,
+ 2, 97, :_reduce_80,
+ 5, 119, :_reduce_81,
+ 4, 119, :_reduce_82,
+ 0, 120, :_reduce_none,
+ 2, 120, :_reduce_84,
+ 4, 120, :_reduce_85,
+ 3, 120, :_reduce_86,
+ 6, 98, :_reduce_87,
+ 5, 98, :_reduce_88,
+ 0, 121, :_reduce_none,
+ 4, 121, :_reduce_90,
+ 3, 121, :_reduce_91,
+ 5, 96, :_reduce_92,
+ 1, 122, :_reduce_93,
+ 2, 122, :_reduce_94,
+ 5, 123, :_reduce_95,
+ 4, 123, :_reduce_96,
+ 1, 124, :_reduce_97,
1, 89, :_reduce_none,
- 1, 89, :_reduce_none,
- 1, 94, :_reduce_164,
- 3, 103, :_reduce_165,
- 4, 103, :_reduce_166,
- 2, 103, :_reduce_167,
- 3, 106, :_reduce_168,
- 4, 106, :_reduce_169,
- 2, 106, :_reduce_170,
- 1, 150, :_reduce_171,
- 3, 150, :_reduce_172,
- 3, 151, :_reduce_173,
- 1, 112, :_reduce_none,
- 1, 112, :_reduce_none,
- 1, 152, :_reduce_176,
- 2, 153, :_reduce_177,
- 1, 154, :_reduce_178,
- 1, 156, :_reduce_179,
- 1, 157, :_reduce_180,
- 2, 155, :_reduce_181,
- 1, 158, :_reduce_182,
- 1, 159, :_reduce_183,
- 2, 159, :_reduce_184,
- 1, 111, :_reduce_185,
- 1, 109, :_reduce_186,
- 1, 110, :_reduce_187,
- 1, 105, :_reduce_188,
- 1, 104, :_reduce_189,
- 1, 107, :_reduce_190,
- 0, 113, :_reduce_none,
- 1, 113, :_reduce_192,
- 0, 133, :_reduce_none,
- 1, 133, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
- 1, 141, :_reduce_none,
+ 4, 89, :_reduce_99,
+ 1, 126, :_reduce_100,
+ 3, 126, :_reduce_101,
+ 3, 125, :_reduce_102,
+ 1, 84, :_reduce_103,
+ 6, 84, :_reduce_104,
+ 6, 84, :_reduce_105,
+ 5, 84, :_reduce_106,
+ 5, 84, :_reduce_107,
+ 5, 84, :_reduce_108,
+ 4, 131, :_reduce_109,
+ 1, 132, :_reduce_110,
+ 1, 128, :_reduce_111,
+ 3, 128, :_reduce_112,
+ 1, 127, :_reduce_113,
+ 2, 127, :_reduce_114,
+ 6, 95, :_reduce_115,
+ 2, 95, :_reduce_116,
+ 3, 133, :_reduce_117,
+ 3, 133, :_reduce_118,
+ 1, 134, :_reduce_none,
+ 1, 134, :_reduce_none,
+ 0, 130, :_reduce_121,
+ 1, 130, :_reduce_122,
+ 3, 130, :_reduce_123,
+ 1, 136, :_reduce_none,
+ 1, 136, :_reduce_none,
+ 1, 136, :_reduce_none,
+ 3, 135, :_reduce_127,
+ 3, 135, :_reduce_128,
+ 6, 99, :_reduce_129,
+ 5, 99, :_reduce_130,
+ 7, 100, :_reduce_131,
+ 6, 100, :_reduce_132,
+ 1, 140, :_reduce_none,
+ 2, 140, :_reduce_134,
1, 141, :_reduce_none,
1, 141, :_reduce_none,
- 0, 80, :_reduce_209 ]
-
-racc_reduce_n = 210
-
-racc_shift_n = 365
+ 6, 101, :_reduce_137,
+ 5, 101, :_reduce_138,
+ 1, 142, :_reduce_139,
+ 3, 142, :_reduce_140,
+ 1, 144, :_reduce_141,
+ 1, 144, :_reduce_142,
+ 1, 144, :_reduce_143,
+ 1, 144, :_reduce_none,
+ 1, 143, :_reduce_none,
+ 2, 143, :_reduce_146,
+ 1, 138, :_reduce_147,
+ 1, 138, :_reduce_148,
+ 1, 139, :_reduce_149,
+ 2, 139, :_reduce_150,
+ 4, 139, :_reduce_151,
+ 1, 118, :_reduce_152,
+ 3, 118, :_reduce_153,
+ 3, 145, :_reduce_154,
+ 1, 145, :_reduce_155,
+ 1, 88, :_reduce_none,
+ 1, 88, :_reduce_none,
+ 1, 93, :_reduce_158,
+ 3, 102, :_reduce_159,
+ 4, 102, :_reduce_160,
+ 2, 102, :_reduce_161,
+ 3, 105, :_reduce_162,
+ 4, 105, :_reduce_163,
+ 2, 105, :_reduce_164,
+ 1, 146, :_reduce_165,
+ 3, 146, :_reduce_166,
+ 3, 147, :_reduce_167,
+ 1, 111, :_reduce_none,
+ 1, 111, :_reduce_none,
+ 1, 148, :_reduce_170,
+ 2, 149, :_reduce_171,
+ 1, 150, :_reduce_172,
+ 1, 152, :_reduce_173,
+ 1, 153, :_reduce_174,
+ 2, 151, :_reduce_175,
+ 1, 154, :_reduce_176,
+ 1, 155, :_reduce_177,
+ 2, 155, :_reduce_178,
+ 1, 110, :_reduce_179,
+ 1, 108, :_reduce_180,
+ 1, 109, :_reduce_181,
+ 1, 104, :_reduce_182,
+ 1, 103, :_reduce_183,
+ 1, 106, :_reduce_184,
+ 0, 112, :_reduce_none,
+ 1, 112, :_reduce_186,
+ 0, 129, :_reduce_none,
+ 1, 129, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 1, 137, :_reduce_none,
+ 0, 79, :_reduce_203 ]
+
+racc_reduce_n = 204
+
+racc_shift_n = 355
racc_token_table = {
false => 0,
@@ -812,16 +800,15 @@ racc_token_table = {
:IN => 66,
:UNLESS => 67,
:PIPE => 68,
- :LAMBDA => 69,
- :SELBRACE => 70,
- :LOW => 71,
- :HIGH => 72,
- :CALL => 73,
- :MODULO => 74,
- :TITLE_COLON => 75,
- :CASE_COLON => 76 }
+ :SELBRACE => 69,
+ :LOW => 70,
+ :HIGH => 71,
+ :CALL => 72,
+ :MODULO => 73,
+ :TITLE_COLON => 74,
+ :CASE_COLON => 75 }
-racc_nt_base = 77
+racc_nt_base = 76
racc_use_result_var = true
@@ -911,7 +898,6 @@ Racc_token_to_s_table = [
"IN",
"UNLESS",
"PIPE",
- "LAMBDA",
"SELBRACE",
"LOW",
"HIGH",
@@ -959,10 +945,7 @@ Racc_token_to_s_table = [
"lambda",
"call_method_expression",
"named_access",
- "lambda_j8",
- "lambda_ruby",
"lambda_parameter_list",
- "optional_farrow",
"lambda_rest",
"parameters",
"if_part",
@@ -1429,71 +1412,45 @@ module_eval(<<'.,.,', 'egrammar.ra', 197)
end
.,.,
-# reduce 75 omitted
-
-# reduce 76 omitted
-
-module_eval(<<'.,.,', 'egrammar.ra', 212)
- def _reduce_77(val, _values, result)
- result = Factory.LAMBDA(val[0], val[2])
+module_eval(<<'.,.,', 'egrammar.ra', 209)
+ def _reduce_75(val, _values, result)
+ result = Factory.LAMBDA(val[0], val[1])
# loc result, val[1] # TODO
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 217)
- def _reduce_78(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 214)
+ def _reduce_76(val, _values, result)
result = val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 218)
- def _reduce_79(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 215)
+ def _reduce_77(val, _values, result)
result = nil
result
end
.,.,
-# reduce 80 omitted
-
-# reduce 81 omitted
-
-module_eval(<<'.,.,', 'egrammar.ra', 228)
- def _reduce_82(val, _values, result)
- result = Factory.LAMBDA(val[1], val[2])
- loc result, val[0], val[3]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'egrammar.ra', 232)
- def _reduce_83(val, _values, result)
- result = Factory.LAMBDA(val[1], nil)
- loc result, val[0], val[2]
-
- result
- end
-.,.,
-
-module_eval(<<'.,.,', 'egrammar.ra', 238)
- def _reduce_84(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 219)
+ def _reduce_78(val, _values, result)
result = []
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 239)
- def _reduce_85(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 220)
+ def _reduce_79(val, _values, result)
result = val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 249)
- def _reduce_86(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 230)
+ def _reduce_80(val, _values, result)
result = val[1]
loc(result, val[0], val[1])
@@ -1501,8 +1458,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 249)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 256)
- def _reduce_87(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 237)
+ def _reduce_81(val, _values, result)
result = Factory.IF(val[0], Factory.block_or_expression(*val[2]), val[4])
loc(result, val[0], (val[4] ? val[4] : val[3]))
@@ -1510,8 +1467,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 256)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 260)
- def _reduce_88(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 241)
+ def _reduce_82(val, _values, result)
result = Factory.IF(val[0], nil, val[3])
loc(result, val[0], (val[3] ? val[3] : val[2]))
@@ -1519,10 +1476,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 260)
end
.,.,
-# reduce 89 omitted
+# reduce 83 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 268)
- def _reduce_90(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 249)
+ def _reduce_84(val, _values, result)
result = val[1]
loc(result, val[0], val[1])
@@ -1530,8 +1487,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 268)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 272)
- def _reduce_91(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 253)
+ def _reduce_85(val, _values, result)
result = Factory.block_or_expression(*val[2])
loc result, val[0], val[3]
@@ -1539,16 +1496,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 272)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 276)
- def _reduce_92(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 257)
+ def _reduce_86(val, _values, result)
result = nil # don't think a nop is needed here either
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 285)
- def _reduce_93(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 266)
+ def _reduce_87(val, _values, result)
result = Factory.UNLESS(val[1], Factory.block_or_expression(*val[3]), val[5])
loc result, val[0], val[4]
@@ -1556,8 +1513,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 285)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 289)
- def _reduce_94(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 270)
+ def _reduce_88(val, _values, result)
result = Factory.UNLESS(val[1], nil, nil)
loc result, val[0], val[4]
@@ -1565,10 +1522,10 @@ module_eval(<<'.,.,', 'egrammar.ra', 289)
end
.,.,
-# reduce 95 omitted
+# reduce 89 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 299)
- def _reduce_96(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 280)
+ def _reduce_90(val, _values, result)
result = Factory.block_or_expression(*val[2])
loc result, val[0], val[3]
@@ -1576,16 +1533,16 @@ module_eval(<<'.,.,', 'egrammar.ra', 299)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 303)
- def _reduce_97(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 284)
+ def _reduce_91(val, _values, result)
result = nil # don't think a nop is needed here either
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 311)
- def _reduce_98(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 292)
+ def _reduce_92(val, _values, result)
result = Factory.CASE(val[1], *val[3])
loc result, val[0], val[4]
@@ -1593,22 +1550,22 @@ module_eval(<<'.,.,', 'egrammar.ra', 311)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 317)
- def _reduce_99(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 298)
+ def _reduce_93(val, _values, result)
result = [val[0]]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 318)
- def _reduce_100(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 299)
+ def _reduce_94(val, _values, result)
result = val[0].push val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 323)
- def _reduce_101(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 304)
+ def _reduce_95(val, _values, result)
result = Factory.WHEN(val[0], val[3])
loc result, val[1], val[4]
@@ -1616,8 +1573,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 323)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 327)
- def _reduce_102(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 308)
+ def _reduce_96(val, _values, result)
result = Factory.WHEN(val[0], nil)
loc result, val[1], val[3]
@@ -1625,54 +1582,54 @@ module_eval(<<'.,.,', 'egrammar.ra', 327)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 331)
- def _reduce_103(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 312)
+ def _reduce_97(val, _values, result)
result = val[0]
result
end
.,.,
-# reduce 104 omitted
+# reduce 98 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 342)
- def _reduce_105(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 323)
+ def _reduce_99(val, _values, result)
result = val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 347)
- def _reduce_106(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 328)
+ def _reduce_100(val, _values, result)
result = [val[0]]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 348)
- def _reduce_107(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 329)
+ def _reduce_101(val, _values, result)
result = val[0].push val[2]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 353)
- def _reduce_108(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 334)
+ def _reduce_102(val, _values, result)
result = Factory.MAP(val[0], val[2]) ; loc result, val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 369)
- def _reduce_109(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 350)
+ def _reduce_103(val, _values, result)
result = val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 372)
- def _reduce_110(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 353)
+ def _reduce_104(val, _values, result)
result = case Factory.resource_shape(val[1])
when :resource, :class
tmp = Factory.RESOURCE(Factory.fqn(token_text(val[1])), val[3])
@@ -1691,8 +1648,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 372)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 387)
- def _reduce_111(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 368)
+ def _reduce_105(val, _values, result)
result = case Factory.resource_shape(val[1])
when :resource, :class
error "Defaults are not virtualizable"
@@ -1708,8 +1665,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 387)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 399)
- def _reduce_112(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 380)
+ def _reduce_106(val, _values, result)
result = case Factory.resource_shape(val[0])
when :resource, :class
Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
@@ -1726,8 +1683,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 399)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 412)
- def _reduce_113(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 393)
+ def _reduce_107(val, _values, result)
result = case Factory.resource_shape(val[0])
when :resource, :class
# This catches deprecated syntax.
@@ -1746,8 +1703,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 412)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 427)
- def _reduce_114(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 408)
+ def _reduce_108(val, _values, result)
result = Factory.RESOURCE(Factory.fqn(token_text(val[0])), val[2])
loc result, val[0], val[4]
@@ -1755,50 +1712,50 @@ module_eval(<<'.,.,', 'egrammar.ra', 427)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 432)
- def _reduce_115(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 413)
+ def _reduce_109(val, _values, result)
result = Factory.RESOURCE_BODY(val[0], val[2])
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 434)
- def _reduce_116(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 415)
+ def _reduce_110(val, _values, result)
result = val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 437)
- def _reduce_117(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 418)
+ def _reduce_111(val, _values, result)
result = [val[0]]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 438)
- def _reduce_118(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 419)
+ def _reduce_112(val, _values, result)
result = val[0].push val[2]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 443)
- def _reduce_119(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 424)
+ def _reduce_113(val, _values, result)
result = :virtual
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 444)
- def _reduce_120(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 425)
+ def _reduce_114(val, _values, result)
result = :exported
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 456)
- def _reduce_121(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 437)
+ def _reduce_115(val, _values, result)
result = Factory.COLLECT(val[0], val[1], val[3])
loc result, val[0], val[5]
@@ -1806,8 +1763,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 456)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 460)
- def _reduce_122(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 441)
+ def _reduce_116(val, _values, result)
result = Factory.COLLECT(val[0], val[1], [])
loc result, val[0], val[1]
@@ -1815,53 +1772,53 @@ module_eval(<<'.,.,', 'egrammar.ra', 460)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 465)
- def _reduce_123(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 446)
+ def _reduce_117(val, _values, result)
result = Factory.VIRTUAL_QUERY(val[1]) ; loc result, val[0], val[2]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 466)
- def _reduce_124(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 447)
+ def _reduce_118(val, _values, result)
result = Factory.EXPORTED_QUERY(val[1]) ; loc result, val[0], val[2]
result
end
.,.,
-# reduce 125 omitted
+# reduce 119 omitted
-# reduce 126 omitted
+# reduce 120 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 479)
- def _reduce_127(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 460)
+ def _reduce_121(val, _values, result)
result = []
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 480)
- def _reduce_128(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 461)
+ def _reduce_122(val, _values, result)
result = [val[0]]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 481)
- def _reduce_129(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 462)
+ def _reduce_123(val, _values, result)
result = val[0].push(val[2])
result
end
.,.,
-# reduce 130 omitted
+# reduce 124 omitted
-# reduce 131 omitted
+# reduce 125 omitted
-# reduce 132 omitted
+# reduce 126 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 497)
- def _reduce_133(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 478)
+ def _reduce_127(val, _values, result)
result = Factory.ATTRIBUTE_OP(val[0][:value], :'=>', val[2])
loc result, val[0], val[2]
@@ -1869,8 +1826,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 497)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 501)
- def _reduce_134(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 482)
+ def _reduce_128(val, _values, result)
result = Factory.ATTRIBUTE_OP(val[0][:value], :'+>', val[2])
loc result, val[0], val[2]
@@ -1878,8 +1835,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 501)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 511)
- def _reduce_135(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 492)
+ def _reduce_129(val, _values, result)
result = Factory.DEFINITION(classname(val[1][:value]), val[2], val[4])
loc result, val[0], val[5]
@lexer.indefine = false
@@ -1888,8 +1845,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 511)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 516)
- def _reduce_136(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 497)
+ def _reduce_130(val, _values, result)
result = Factory.DEFINITION(classname(val[1][:value]), val[2], nil)
loc result, val[0], val[4]
@lexer.indefine = false
@@ -1898,8 +1855,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 516)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 531)
- def _reduce_137(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 512)
+ def _reduce_131(val, _values, result)
@lexer.namepop
result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), val[5])
loc result, val[0], val[6]
@@ -1908,8 +1865,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 531)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 536)
- def _reduce_138(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 517)
+ def _reduce_132(val, _values, result)
@lexer.namepop
result = Factory.HOSTCLASS(classname(val[1][:value]), val[2], token_text(val[3]), nil)
loc result, val[0], val[5]
@@ -1918,21 +1875,21 @@ module_eval(<<'.,.,', 'egrammar.ra', 536)
end
.,.,
-# reduce 139 omitted
+# reduce 133 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 544)
- def _reduce_140(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 525)
+ def _reduce_134(val, _values, result)
result = val[1]
result
end
.,.,
-# reduce 141 omitted
+# reduce 135 omitted
-# reduce 142 omitted
+# reduce 136 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 561)
- def _reduce_143(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 542)
+ def _reduce_137(val, _values, result)
result = Factory.NODE(val[1], val[2], val[4])
loc result, val[0], val[5]
@@ -1940,8 +1897,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 561)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 565)
- def _reduce_144(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 546)
+ def _reduce_138(val, _values, result)
result = Factory.NODE(val[1], val[2], nil)
loc result, val[0], val[4]
@@ -1949,307 +1906,319 @@ module_eval(<<'.,.,', 'egrammar.ra', 565)
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 575)
- def _reduce_145(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 556)
+ def _reduce_139(val, _values, result)
result = [result]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 576)
- def _reduce_146(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 557)
+ def _reduce_140(val, _values, result)
result = val[0].push(val[2])
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 581)
- def _reduce_147(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 562)
+ def _reduce_141(val, _values, result)
result = Factory.fqn(val[0][:value]); loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 582)
- def _reduce_148(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 563)
+ def _reduce_142(val, _values, result)
result = val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 583)
- def _reduce_149(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 564)
+ def _reduce_143(val, _values, result)
result = Factory.literal(:default); loc result, val[0]
result
end
.,.,
-# reduce 150 omitted
+# reduce 144 omitted
-# reduce 151 omitted
+# reduce 145 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 589)
- def _reduce_152(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 570)
+ def _reduce_146(val, _values, result)
result = val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 594)
- def _reduce_153(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 575)
+ def _reduce_147(val, _values, result)
result = val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 595)
- def _reduce_154(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 576)
+ def _reduce_148(val, _values, result)
result = val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 599)
- def _reduce_155(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 580)
+ def _reduce_149(val, _values, result)
result = []
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 600)
- def _reduce_156(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 581)
+ def _reduce_150(val, _values, result)
result = []
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 601)
- def _reduce_157(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 582)
+ def _reduce_151(val, _values, result)
result = val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 605)
- def _reduce_158(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 586)
+ def _reduce_152(val, _values, result)
result = [val[0]]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 606)
- def _reduce_159(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 587)
+ def _reduce_153(val, _values, result)
result = val[0].push(val[2])
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 610)
- def _reduce_160(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 591)
+ def _reduce_154(val, _values, result)
result = Factory.PARAM(val[0][:value], val[2]) ; loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 611)
- def _reduce_161(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 592)
+ def _reduce_155(val, _values, result)
result = Factory.PARAM(val[0][:value]); loc result, val[0]
result
end
.,.,
-# reduce 162 omitted
+# reduce 156 omitted
-# reduce 163 omitted
+# reduce 157 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 624)
- def _reduce_164(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 605)
+ def _reduce_158(val, _values, result)
result = Factory.fqn(val[0][:value]).var ; loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 630)
- def _reduce_165(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 611)
+ def _reduce_159(val, _values, result)
result = Factory.LIST(val[1]); loc result, val[0], val[2]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 631)
- def _reduce_166(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 612)
+ def _reduce_160(val, _values, result)
result = Factory.LIST(val[1]); loc result, val[0], val[3]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 632)
- def _reduce_167(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 613)
+ def _reduce_161(val, _values, result)
result = Factory.literal([]) ; loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 635)
- def _reduce_168(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 616)
+ def _reduce_162(val, _values, result)
result = Factory.HASH(val[1]); loc result, val[0], val[2]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 636)
- def _reduce_169(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 617)
+ def _reduce_163(val, _values, result)
result = Factory.HASH(val[1]); loc result, val[0], val[3]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 637)
- def _reduce_170(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 618)
+ def _reduce_164(val, _values, result)
result = Factory.literal({}) ; loc result, val[0], val[3]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 640)
- def _reduce_171(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 621)
+ def _reduce_165(val, _values, result)
result = [val[0]]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 641)
- def _reduce_172(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 622)
+ def _reduce_166(val, _values, result)
result = val[0].push val[2]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 644)
- def _reduce_173(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 625)
+ def _reduce_167(val, _values, result)
result = Factory.KEY_ENTRY(val[0], val[2]); loc result, val[1]
result
end
.,.,
-# reduce 174 omitted
+# reduce 168 omitted
-# reduce 175 omitted
+# reduce 169 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 650)
- def _reduce_176(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 631)
+ def _reduce_170(val, _values, result)
result = Factory.literal(val[0][:value]) ; loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 651)
- def _reduce_177(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 632)
+ def _reduce_171(val, _values, result)
result = Factory.string(val[0], *val[1]) ; loc result, val[0], val[1][-1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 652)
- def _reduce_178(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 633)
+ def _reduce_172(val, _values, result)
result = Factory.literal(val[0][:value]); loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 653)
- def _reduce_179(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 634)
+ def _reduce_173(val, _values, result)
result = Factory.literal(val[0][:value]); loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 654)
- def _reduce_180(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 635)
+ def _reduce_174(val, _values, result)
result = Factory.literal(val[0][:value]); loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 655)
- def _reduce_181(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 636)
+ def _reduce_175(val, _values, result)
result = [val[0]] + val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 656)
- def _reduce_182(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 637)
+ def _reduce_176(val, _values, result)
result = Factory.TEXT(val[0])
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 659)
- def _reduce_183(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 640)
+ def _reduce_177(val, _values, result)
result = [val[0]]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 660)
- def _reduce_184(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 641)
+ def _reduce_178(val, _values, result)
result = [val[0]] + val[1]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 662)
- def _reduce_185(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 643)
+ def _reduce_179(val, _values, result)
result = Factory.QNAME_OR_NUMBER(val[0][:value]) ; loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 663)
- def _reduce_186(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 644)
+ def _reduce_180(val, _values, result)
result = Factory.QREF(val[0][:value]) ; loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 664)
- def _reduce_187(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 645)
+ def _reduce_181(val, _values, result)
result = Factory.literal(:undef); loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 665)
- def _reduce_188(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 646)
+ def _reduce_182(val, _values, result)
result = Factory.literal(:default); loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 670)
- def _reduce_189(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 651)
+ def _reduce_183(val, _values, result)
result = Factory.literal(val[0][:value]) ; loc result, val[0]
result
end
.,.,
-module_eval(<<'.,.,', 'egrammar.ra', 673)
- def _reduce_190(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 654)
+ def _reduce_184(val, _values, result)
result = Factory.literal(val[0][:value]); loc result, val[0]
result
end
.,.,
-# reduce 191 omitted
+# reduce 185 omitted
-module_eval(<<'.,.,', 'egrammar.ra', 679)
- def _reduce_192(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 660)
+ def _reduce_186(val, _values, result)
result = nil
result
end
.,.,
+# reduce 187 omitted
+
+# reduce 188 omitted
+
+# reduce 189 omitted
+
+# reduce 190 omitted
+
+# reduce 191 omitted
+
+# reduce 192 omitted
+
# reduce 193 omitted
# reduce 194 omitted
@@ -2270,20 +2239,8 @@ module_eval(<<'.,.,', 'egrammar.ra', 679)
# reduce 202 omitted
-# reduce 203 omitted
-
-# reduce 204 omitted
-
-# reduce 205 omitted
-
-# reduce 206 omitted
-
-# reduce 207 omitted
-
-# reduce 208 omitted
-
-module_eval(<<'.,.,', 'egrammar.ra', 702)
- def _reduce_209(val, _values, result)
+module_eval(<<'.,.,', 'egrammar.ra', 683)
+ def _reduce_203(val, _values, result)
result = nil
result
end
diff --git a/lib/puppet/pops/parser/lexer.rb b/lib/puppet/pops/parser/lexer.rb
index 1e090f7bc..994c4643f 100644
--- a/lib/puppet/pops/parser/lexer.rb
+++ b/lib/puppet/pops/parser/lexer.rb
@@ -148,8 +148,8 @@ class Puppet::Pops::Parser::Lexer
TOKENS.add_tokens(
'[' => :LBRACK,
']' => :RBRACK,
- # '{' => :LBRACE, # Specialized to handle lambda
- '}' => :RBRACE,
+ # '{' => :LBRACE, # Specialized to handle lambda and brace count
+ # '}' => :RBRACE, # Specialized to handle brace count
'(' => :LPAREN,
')' => :RPAREN,
'=' => :EQUALS,
@@ -194,7 +194,6 @@ class Puppet::Pops::Parser::Lexer
"<dqstring between two interpolations>" => :DQMID,
"<dqstring after final interpolation>" => :DQPOST,
"<boolean>" => :BOOLEAN,
- "<lambda start>" => :LAMBDA, # A LBRACE followed by '|'
"<select start>" => :SELBRACE # A QMARK followed by '{'
)
@@ -214,10 +213,6 @@ class Puppet::Pops::Parser::Lexer
REGEX_INTRODUCING_TOKENS.include? context[:after]
end
- IN_STRING_INTERPOLATION = Proc.new do |context|
- context[:string_interpolation_depth] > 0
- end
-
DASHED_VARIABLES_ALLOWED = Proc.new do |context|
Puppet[:allow_variables_with_dashes]
end
@@ -227,20 +222,6 @@ class Puppet::Pops::Parser::Lexer
end
end
- # LBRACE needs look ahead to differentiate between '{' and a '{'
- # followed by a '|' (start of lambda) The racc grammar can only do one
- # token lookahead.
- #
- TOKENS.add_token :LBRACE, /\{/ do | lexer, value |
- if lexer.match?(/[ \t\r]*\|/)
- [TOKENS[:LAMBDA], value]
- elsif lexer.lexing_context[:after] == :QMARK
- [TOKENS[:SELBRACE], value]
- else
- [TOKENS[:LBRACE], value]
- end
- end
-
# Numbers are treated separately from names, so that they may contain dots.
TOKENS.add_token :NUMBER, %r{\b(?:0[xX][0-9A-Fa-f]+|0?\d+(?:\.\d+)?(?:[eE]-?\d+)?)\b} do |lexer, value|
lexer.assert_numeric(value)
@@ -305,10 +286,31 @@ class Puppet::Pops::Parser::Lexer
lexer.tokenize_interpolated_string(DQ_initial_token_types)
end
- TOKENS.add_token :DQCONT, /\}/ do |lexer, value|
- lexer.tokenize_interpolated_string(DQ_continuation_token_types)
+
+ # LBRACE needs look ahead to differentiate between '{' and a '{'
+ # followed by a '|' (start of lambda) The racc grammar can only do one
+ # token lookahead.
+ #
+ TOKENS.add_token :LBRACE, "{" do |lexer, value|
+ lexer.lexing_context[:brace_count] += 1
+ if lexer.lexing_context[:after] == :QMARK
+ [TOKENS[:SELBRACE], value]
+ else
+ [TOKENS[:LBRACE], value]
+ end
+ end
+
+ # RBRACE needs to differentiate between a regular brace that is part of
+ # syntax and one that is the ending of a string interpolation.
+ TOKENS.add_token :RBRACE, "}" do |lexer, value|
+ context = lexer.lexing_context
+ if context[:interpolation_stack].empty? || context[:brace_count] != context[:interpolation_stack][-1]
+ context[:brace_count] -= 1
+ [TOKENS[:RBRACE], value]
+ else
+ lexer.tokenize_interpolated_string(DQ_continuation_token_types)
+ end
end
- TOKENS[:DQCONT].acceptable_when Contextual::IN_STRING_INTERPOLATION
TOKENS.add_token :DOLLAR_VAR_WITH_DASH, %r{\$(?:::)?(?:[-\w]+::)*[-\w]+} do |lexer, value|
lexer.warn_if_variable_has_hyphen(value)
@@ -339,9 +341,21 @@ class Puppet::Pops::Parser::Lexer
# reference.
#
if lexer.match?(%r{[ \t\r]*\(})
- [TOKENS[:NAME],value]
+ # followed by ( is a function call
+ [TOKENS[:NAME], value]
+
+ elsif kwd_token = KEYWORDS.lookup(value)
+ # true, false, if, unless, case, and undef are keywords that cannot be used as variables
+ # but node, and several others are variables
+ if [ :TRUE, :FALSE ].include?(kwd_token.name)
+ [ TOKENS[:BOOLEAN], eval(value) ]
+ elsif [ :IF, :UNLESS, :CASE, :UNDEF ].include?(kwd_token.name)
+ [kwd_token, value]
+ else
+ [TOKENS[:VARIABLE], value]
+ end
else
- [TOKENS[:VARIABLE],value]
+ [TOKENS[:VARIABLE], value]
end
end
@@ -404,7 +418,7 @@ class Puppet::Pops::Parser::Lexer
def file=(file)
@file = file
- contents = File.exists?(file) ? File.read(file) : ""
+ contents = Puppet::FileSystem::File.exist?(file) ? File.read(file) : ""
@scanner = StringScanner.new(contents)
@locator = Locator.new(contents, multibyte?)
end
@@ -501,7 +515,8 @@ class Puppet::Pops::Parser::Lexer
:start_of_line => true,
:offset => 0, # byte offset before where token starts
:end_offset => 0, # byte offset after scanned token
- :string_interpolation_depth => 0
+ :brace_count => 0, # nested depth of braces
+ :interpolation_stack => [] # matching interpolation brace level
}
end
@@ -592,8 +607,11 @@ class Puppet::Pops::Parser::Lexer
end
lexing_context[:after] = final_token.name unless newline
- lexing_context[:string_interpolation_depth] += 1 if final_token.name == :DQPRE
- lexing_context[:string_interpolation_depth] -= 1 if final_token.name == :DQPOST
+ if final_token.name == :DQPRE
+ lexing_context[:interpolation_stack] << lexing_context[:brace_count]
+ elsif final_token.name == :DQPOST
+ lexing_context[:interpolation_stack].pop
+ end
value = token_value[:value]
diff --git a/lib/puppet/pops/parser/parser_support.rb b/lib/puppet/pops/parser/parser_support.rb
index db35aad01..a22de7161 100644
--- a/lib/puppet/pops/parser/parser_support.rb
+++ b/lib/puppet/pops/parser/parser_support.rb
@@ -63,7 +63,7 @@ class Puppet::Pops::Parser::Parser
# Parses a file expected to contain pp DSL logic.
def parse_file(file)
- unless FileTest.exist?(file)
+ unless Puppet::FileSystem::File.exist?(file)
unless file =~ /\.pp$/
file = file + ".pp"
end
diff --git a/lib/puppet/pops/patterns.rb b/lib/puppet/pops/patterns.rb
index d18384fcb..b1c76ad43 100644
--- a/lib/puppet/pops/patterns.rb
+++ b/lib/puppet/pops/patterns.rb
@@ -18,18 +18,18 @@ module Puppet::Pops::Patterns
# NAME matches a name the same way as the lexer.
# This name includes hyphen, which may be illegal in variables, and names in general.
- NAME = %r{((::)?[a-z0-9][-\w]*)(::[a-z0-9][-\w]*)*}
+ NAME = %r{\A((::)?[a-z0-9]\w*)(::[a-z0-9]\w*)*\z}
# CLASSREF_EXT matches a class reference the same way as the lexer - i.e. the external source form
# where each part must start with a capital letter A-Z.
# This name includes hyphen, which may be illegal in some cases.
#
- CLASSREF_EXT = %r{((::){0,1}[A-Z][-\w]*)+}
+ CLASSREF_EXT = %r{\A((::){0,1}[A-Z][-\w]*)+\z}
- # CLASSREF matches a class reference the way it is represented internall in the
+ # CLASSREF matches a class reference the way it is represented internally in the
# model (i.e. in lower case).
# This name includes hyphen, which may be illegal in some cases.
#
- CLASSREF = %r{((::){0,1}[a-z][-\w]*)+}
+ CLASSREF = %r{\A((::){0,1}[a-z][-\w]*)+\z}
end
diff --git a/lib/puppet/pops/utils.rb b/lib/puppet/pops/utils.rb
index 104a26951..01a540432 100644
--- a/lib/puppet/pops/utils.rb
+++ b/lib/puppet/pops/utils.rb
@@ -35,7 +35,7 @@ module Puppet::Pops::Utils
radix = 10
if match[1].to_s.length > 0
radix = 16
- elsif match[2].to_s.length > 0 && match[2][0] == '0'
+ elsif match[2].to_s.length > 1 && match[2][0] == '0'
radix = 8
end
[Integer(match[0], radix), radix]
diff --git a/lib/puppet/pops/validation/checker3_1.rb b/lib/puppet/pops/validation/checker3_1.rb
index 65cbbbeaf..839df5044 100644
--- a/lib/puppet/pops/validation/checker3_1.rb
+++ b/lib/puppet/pops/validation/checker3_1.rb
@@ -20,7 +20,7 @@ class Puppet::Pops::Validation::Checker3_1
def initialize(diagnostics_producer)
@@check_visitor ||= Puppet::Pops::Visitor.new(nil, "check", 0, 0)
@@rvalue_visitor ||= Puppet::Pops::Visitor.new(nil, "rvalue", 0, 0)
- @@hostname_visitor ||= Puppet::Pops::Visitor.new(nil, "hostname", 1, 1)
+ @@hostname_visitor ||= Puppet::Pops::Visitor.new(nil, "hostname", 1, 2)
@@assignment_visitor ||= Puppet::Pops::Visitor.new(nil, "assign", 0, 1)
@@query_visitor ||= Puppet::Pops::Visitor.new(nil, "query", 0, 0)
@@top_visitor ||= Puppet::Pops::Visitor.new(nil, "top", 1, 1)
@@ -45,8 +45,9 @@ class Puppet::Pops::Validation::Checker3_1
end
# Performs check if this is a vaid hostname expression
- def hostname(o, semantic)
- @@hostname_visitor.visit_this(self, o, semantic)
+ # @param single_feature_name [String, nil] the name of a single valued hostname feature of the value's container. e.g. 'parent'
+ def hostname(o, semantic, single_feature_name = nil)
+ @@hostname_visitor.visit_this(self, o, semantic, single_feature_name)
end
# Performs check if this is valid as a query
@@ -125,7 +126,7 @@ class Puppet::Pops::Validation::Checker3_1
case o.left_expr
when Model::QualifiedName
# allows many keys, but the name should really be a QualifiedReference
- acceptor.accept(Issues::DEPRECATED_NAME_AS_TYPE, o, :name => o.value)
+ acceptor.accept(Issues::DEPRECATED_NAME_AS_TYPE, o, :name => o.left_expr.value)
when Model::QualifiedReference
# ok, allows many - this is a resource reference
@@ -255,6 +256,7 @@ class Puppet::Pops::Validation::Checker3_1
def check_NodeDefinition(o)
# Check that hostnames are valid hostnames (or regular expressons)
hostname(o.host_matches, o)
+ hostname(o.parent, o, 'parent') unless o.parent.nil?
top(o.eContainer, o)
end
@@ -385,11 +387,14 @@ class Puppet::Pops::Validation::Checker3_1
#--- HOSTNAME CHECKS
# Transforms Array of host matching expressions into a (Ruby) array of AST::HostName
- def hostname_Array(o, semantic)
- o.each {|x| hostname x, semantic }
+ def hostname_Array(o, semantic, single_feature_name)
+ if single_feature_name
+ acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=>single_feature_name, :container=>semantic})
+ end
+ o.each {|x| hostname(x, semantic, false) }
end
- def hostname_String(o, semantic)
+ def hostname_String(o, semantic, single_feature_name)
# The 3.x checker only checks for illegal characters - if matching /[^-\w.]/ the name is invalid,
# but this allows pathological names like "a..b......c", "----"
# TODO: Investigate if more illegal hostnames should be flagged.
@@ -399,11 +404,11 @@ class Puppet::Pops::Validation::Checker3_1
end
end
- def hostname_LiteralValue(o, semantic)
- hostname_String(o.value.to_s, o)
+ def hostname_LiteralValue(o, semantic, single_feature_name)
+ hostname_String(o.value.to_s, o, single_feature_name)
end
- def hostname_ConcatenatedString(o, semantic)
+ def hostname_ConcatenatedString(o, semantic, single_feature_name)
# Puppet 3.1. only accepts a concatenated string without interpolated expressions
if the_expr = o.segments.index {|s| s.is_a?(Model::TextExpression) }
acceptor.accept(Issues::ILLEGAL_HOSTNAME_INTERPOLATION, o.segments[the_expr].expr)
@@ -413,32 +418,32 @@ class Puppet::Pops::Validation::Checker3_1
else
# corner case, may be ok, but lexer may have replaced with plain string, this is
# here if it does not
- hostname_String(o.segments[0], o.segments[0])
+ hostname_String(o.segments[0], o.segments[0], false)
end
end
- def hostname_QualifiedName(o, semantic)
- hostname_String(o.value.to_s, o)
+ def hostname_QualifiedName(o, semantic, single_feature_name)
+ hostname_String(o.value.to_s, o, single_feature_name)
end
- def hostname_QualifiedReference(o, semantic)
- hostname_String(o.value.to_s, o)
+ def hostname_QualifiedReference(o, semantic, single_feature_name)
+ hostname_String(o.value.to_s, o, single_feature_name)
end
- def hostname_LiteralNumber(o, semantic)
+ def hostname_LiteralNumber(o, semantic, single_feature_name)
# always ok
end
- def hostname_LiteralDefault(o, semantic)
+ def hostname_LiteralDefault(o, semantic, single_feature_name)
# always ok
end
- def hostname_LiteralRegularExpression(o, semantic)
+ def hostname_LiteralRegularExpression(o, semantic, single_feature_name)
# always ok
end
- def hostname_Object(o, semantic)
- acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=>'hostname', :container=>semantic})
+ def hostname_Object(o, semantic, single_feature_name)
+ acceptor.accept(Issues::ILLEGAL_EXPRESSION, o, {:feature=> single_feature_name || 'hostname', :container=>semantic})
end
#---QUERY CHECKS
diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb
index f875e3df0..d88eebea3 100644
--- a/lib/puppet/provider.rb
+++ b/lib/puppet/provider.rb
@@ -40,10 +40,10 @@ class Puppet::Provider
include Puppet::Util::Warnings
extend Puppet::Util::Warnings
- require 'puppet/provider/confiner'
+ require 'puppet/confiner'
require 'puppet/provider/command'
- extend Puppet::Provider::Confiner
+ extend Puppet::Confiner
Puppet::Util.logmethods(self, true)
@@ -148,6 +148,7 @@ class Puppet::Provider
# @raise [Puppet::DevError] if the name does not reference an existing command.
# @return [String] the absolute path to the found executable for the command
# @see which
+ # @api public
def self.command(name)
name = name.intern
@@ -163,7 +164,7 @@ class Puppet::Provider
end
# Confines this provider to be suitable only on hosts where the given commands are present.
- # Also see {Puppet::Provider::Confiner#confine} for other types of confinement of a provider by use of other types of
+ # Also see {Puppet::Confiner#confine} for other types of confinement of a provider by use of other types of
# predicates.
#
# @note It is preferred if the commands are not entered with absolute paths as this allows puppet
@@ -173,6 +174,7 @@ class Puppet::Provider
# be executing on the system. Each command is specified with a name and the path of the executable.
# @return [void]
# @see optional_commands
+ # @api public
#
def self.commands(command_specs)
command_specs.each do |name, path|
@@ -189,6 +191,7 @@ class Puppet::Provider
# be executing on the system. Each command is specified with a name and the path of the executable.
# (@see #has_command)
# @see commands
+ # @api public
def self.optional_commands(hash)
hash.each do |name, target|
has_command(name, target) do
@@ -221,6 +224,7 @@ class Puppet::Provider
# @comment a yield [ ] produces {|| ...} in the signature, do not remove the space.
# @note the name ´has_command´ looks odd in an API context, but makes more sense when seen in the internal
# DSL context where a Provider is declaratively defined.
+ # @api public
#
def self.has_command(name, path, &block)
name = name.intern
@@ -485,7 +489,7 @@ class Puppet::Provider
if @defaults.length > 0
return "Default for " + @defaults.collect do |f, v|
"`#{f}` == `#{[v].flatten.join(', ')}`"
- end.join(" and ") + "."
+ end.sort.join(" and ") + "."
end
end
@@ -493,7 +497,7 @@ class Puppet::Provider
if @commands.length > 0
return "Required binaries: " + @commands.collect do |n, c|
"`#{c}`"
- end.join(", ") + "."
+ end.sort.join(", ") + "."
end
end
@@ -501,7 +505,7 @@ class Puppet::Provider
if features.length > 0
return "Supported features: " + features.collect do |f|
"`#{f}`"
- end.join(", ") + "."
+ end.sort.join(", ") + "."
end
end
@@ -602,6 +606,18 @@ class Puppet::Provider
# fetched state (i.e. what is returned from the {instances} method).
# @param resources_hash [Hash<{String => Puppet::Resource}>] map from name to resource of resources to prefetch
# @return [void]
+ # @api public
+
+ # @comment Document post_resource_eval here as it does not exist anywhere else (called from transaction if implemented)
+ # @!method self.post_resource_eval()
+ # @since 3.4.0
+ # @api public
+ # @abstract A subclass may implement this - it is not implemented in the Provider class
+ # This method may be implemented by a provider in order to perform any
+ # cleanup actions needed. It will be called at the end of the transaction if
+ # any resources in the catalog make use of the provider, regardless of
+ # whether the resources are changed or not and even if resource failures occur.
+ # @return [void]
# @comment Document flush here as it does not exist anywhere (called from transaction if implemented)
# @!method flush()
@@ -609,5 +625,6 @@ class Puppet::Provider
# This method may be implemented by a provider in order to flush properties that has not been individually
# applied to the managed entity's current state.
# @return [void]
+ # @api public
end
diff --git a/lib/puppet/provider/aixobject.rb b/lib/puppet/provider/aixobject.rb
index ed27b4e52..ed27b4e52 100755..100644
--- a/lib/puppet/provider/aixobject.rb
+++ b/lib/puppet/provider/aixobject.rb
diff --git a/lib/puppet/provider/augeas/augeas.rb b/lib/puppet/provider/augeas/augeas.rb
index 2246636da..f0369a420 100644
--- a/lib/puppet/provider/augeas/augeas.rb
+++ b/lib/puppet/provider/augeas/augeas.rb
@@ -125,7 +125,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
end
fail("missing string argument #{narg} for #{cmd}") unless argline[-1]
elsif f == :comparator
- argline << sc.scan(/(==|!=|=~|<|<=|>|>=)/)
+ argline << sc.scan(/(==|!=|=~|<=|>=|<|>)/)
unless argline[-1]
puts sc.rest
fail("invalid comparator for command #{cmd}")
@@ -198,6 +198,17 @@ Puppet::Type.type(:augeas).provide(:augeas) do
end
end
+ def is_numeric?(s)
+ case s
+ when Fixnum
+ true
+ when String
+ s.match(/\A[+-]?\d+?(\.\d+)?\Z/n) == nil ? false : true
+ else
+ false
+ end
+ end
+
# Used by the need_to_run? method to process get filters. Returns
# true if there is a match, false if otherwise
# Assumes a syntax of get /files/path [COMPARATOR] value
@@ -213,10 +224,15 @@ Puppet::Type.type(:augeas).provide(:augeas) do
#check the value in augeas
result = @aug.get(path) || ''
- case comparator
- when "!="
+
+ if ['<', '<=', '>=', '>'].include? comparator and is_numeric?(result) and
+ is_numeric?(arg)
+ resultf = result.to_f
+ argf = arg.to_f
+ return_value = (resultf.send(comparator, argf))
+ elsif comparator == "!="
return_value = (result != arg)
- when "=~"
+ elsif comparator == "=~"
regex = Regexp.new(arg)
return_value = (result =~ regex)
else
@@ -292,7 +308,7 @@ Puppet::Type.type(:augeas).provide(:augeas) do
load_path.flatten!
end
- if File.exists?("#{Puppet[:libdir]}/augeas/lenses")
+ if Puppet::FileSystem::File.exist?("#{Puppet[:libdir]}/augeas/lenses")
load_path << "#{Puppet[:libdir]}/augeas/lenses"
end
diff --git a/lib/puppet/provider/confine.rb b/lib/puppet/provider/confine.rb
index b28d07df3..4804465b0 100644
--- a/lib/puppet/provider/confine.rb
+++ b/lib/puppet/provider/confine.rb
@@ -1,80 +1,6 @@
-# The class that handles testing whether our providers
-# actually work or not.
-require 'puppet/util'
+# Confines have been moved out of the provider as they are also used for other things.
+# This provides backwards compatibility for people still including this old location.
+require 'puppet/provider'
+require 'puppet/confine'
-class Puppet::Provider::Confine
- include Puppet::Util
-
- @tests = {}
-
- class << self
- attr_accessor :name
- end
-
- def self.inherited(klass)
- name = klass.to_s.split("::").pop.downcase.to_sym
- raise "Test #{name} is already defined" if @tests.include?(name)
-
- klass.name = name
-
- @tests[name] = klass
- end
-
- def self.test(name)
- unless @tests[name]
- begin
- require "puppet/provider/confine/#{name}"
- rescue LoadError => detail
- unless detail.to_s =~ /No such file|cannot load such file/i
- warn "Could not load confine test '#{name}': #{detail}"
- end
- # Could not find file
- end
- end
- @tests[name]
- end
-
- attr_reader :values
-
- # Mark that this confine is used for testing binary existence.
- attr_accessor :for_binary
- def for_binary?
- for_binary
- end
-
- # Used for logging.
- attr_accessor :label
-
- def initialize(values)
- values = [values] unless values.is_a?(Array)
- @values = values
- end
-
- # Provide a hook for the message when there's a failure.
- def message(value)
- ""
- end
-
- # Collect the results of all of them.
- def result
- values.collect { |value| pass?(value) }
- end
-
- # Test whether our confine matches.
- def valid?
- values.each do |value|
- unless pass?(value)
- Puppet.debug(label + ": " + message(value))
- return false
- end
- end
-
- return true
- ensure
- reset
- end
-
- # Provide a hook for subclasses.
- def reset
- end
-end
+Puppet::Provider::Confine = Puppet::Confine
diff --git a/lib/puppet/provider/cron/crontab.rb b/lib/puppet/provider/cron/crontab.rb
index 91047af78..91047af78 100755..100644
--- a/lib/puppet/provider/cron/crontab.rb
+++ b/lib/puppet/provider/cron/crontab.rb
diff --git a/lib/puppet/provider/exec.rb b/lib/puppet/provider/exec.rb
index d5cd552fb..b0db9ff94 100644
--- a/lib/puppet/provider/exec.rb
+++ b/lib/puppet/provider/exec.rb
@@ -47,18 +47,17 @@ class Puppet::Provider::Exec < Puppet::Provider
end
end
-
Timeout::timeout(resource[:timeout]) do
# note that we are passing "false" for the "override_locale" parameter, which ensures that the user's
# default/system locale will be respected. Callers may override this behavior by setting locale-related
# environment variables (LANG, LC_ALL, etc.) in their 'environment' configuration.
- output, status = Puppet::Util::SUIDManager.
- run_and_capture(command, resource[:user], resource[:group],
- :override_locale => false,
- :custom_environment => environment)
+ output = Puppet::Util::Execution.execute(command, :failonfail => false, :combine => true,
+ :uid => resource[:user], :gid => resource[:group],
+ :override_locale => false,
+ :custom_environment => environment)
end
# The shell returns 127 if the command is missing.
- if status.exitstatus == 127
+ if output.exitstatus == 127
raise ArgumentError, output
end
@@ -67,7 +66,10 @@ class Puppet::Provider::Exec < Puppet::Provider
self.fail detail.to_s
end
- return output, status
+ # Return output twice as processstatus was returned before, but only exitstatus was ever called.
+ # Output has the exitstatus on it so it is returned instead. This is here twice as changing this
+ # would result in a change to the underlying API.
+ return output, output
end
def extractexe(command)
diff --git a/lib/puppet/provider/exec/posix.rb b/lib/puppet/provider/exec/posix.rb
index 82d6068ea..c552f9a03 100644
--- a/lib/puppet/provider/exec/posix.rb
+++ b/lib/puppet/provider/exec/posix.rb
@@ -1,6 +1,7 @@
require 'puppet/provider/exec'
Puppet::Type.type(:exec).provide :posix, :parent => Puppet::Provider::Exec do
+ has_feature :umask
confine :feature => :posix
defaultfor :feature => :posix
@@ -16,7 +17,7 @@ Puppet::Type.type(:exec).provide :posix, :parent => Puppet::Provider::Exec do
exe = extractexe(command)
if File.expand_path(exe) == exe
- if !File.exists?(exe)
+ if !Puppet::FileSystem::File.exist?(exe)
raise ArgumentError, "Could not find command '#{exe}'"
elsif !File.file?(exe)
raise ArgumentError, "'#{exe}' is a #{File.ftype(exe)}, not a file"
@@ -36,4 +37,12 @@ Puppet::Type.type(:exec).provide :posix, :parent => Puppet::Provider::Exec do
# distinguish not found from not executable
raise ArgumentError, "Could not find command '#{exe}'"
end
+
+ def run(command, check = false)
+ if resource[:umask]
+ Puppet::Util::withumask(resource[:umask]) { super(command, check) }
+ else
+ super(command, check)
+ end
+ end
end
diff --git a/lib/puppet/provider/exec/windows.rb b/lib/puppet/provider/exec/windows.rb
index 05b9af9e9..1c727ef0b 100644
--- a/lib/puppet/provider/exec/windows.rb
+++ b/lib/puppet/provider/exec/windows.rb
@@ -36,7 +36,7 @@ Puppet::Type.type(:exec).provide :windows, :parent => Puppet::Provider::Exec do
exe = extractexe(command)
if absolute_path?(exe)
- if !File.exists?(exe)
+ if !Puppet::FileSystem::File.exist?(exe)
raise ArgumentError, "Could not find command '#{exe}'"
elsif !File.file?(exe)
raise ArgumentError, "'#{exe}' is a #{File.ftype(exe)}, not a file"
diff --git a/lib/puppet/provider/file/posix.rb b/lib/puppet/provider/file/posix.rb
index 5ab84b48b..629a380dd 100644
--- a/lib/puppet/provider/file/posix.rb
+++ b/lib/puppet/provider/file/posix.rb
@@ -2,6 +2,7 @@ Puppet::Type.type(:file).provide :posix do
desc "Uses POSIX functionality to manage file ownership and permissions."
confine :feature => :posix
+ has_features :manages_symlinks
include Puppet::Util::POSIX
include Puppet::Util::Warnings
diff --git a/lib/puppet/provider/file/windows.rb b/lib/puppet/provider/file/windows.rb
index b3475ebe1..350948c47 100644
--- a/lib/puppet/provider/file/windows.rb
+++ b/lib/puppet/provider/file/windows.rb
@@ -2,6 +2,7 @@ Puppet::Type.type(:file).provide :windows do
desc "Uses Microsoft Windows functionality to manage file ownership and permissions."
confine :operatingsystem => :windows
+ has_feature :manages_symlinks if Puppet.features.manages_symlinks?
include Puppet::Util::Warnings
@@ -35,33 +36,37 @@ Puppet::Type.type(:file).provide :windows do
alias :name2uid :name2id
def owner
- return :absent unless resource.exist?
+ return :absent unless resource.stat
get_owner(resource[:path])
end
def owner=(should)
begin
- set_owner(should, resource[:path])
+ path = resource[:links] == :manage ? file.path.to_s : file.readlink
+
+ set_owner(should, path)
rescue => detail
raise Puppet::Error, "Failed to set owner to '#{should}': #{detail}"
end
end
def group
- return :absent unless resource.exist?
+ return :absent unless resource.stat
get_group(resource[:path])
end
def group=(should)
begin
- set_group(should, resource[:path])
+ path = resource[:links] == :manage ? file.path.to_s : file.readlink
+
+ set_group(should, path)
rescue => detail
raise Puppet::Error, "Failed to set group to '#{should}': #{detail}"
end
end
def mode
- if resource.exist?
+ if resource.stat
mode = get_mode(resource[:path])
mode ? mode.to_s(8) : :absent
else
@@ -85,4 +90,10 @@ Puppet::Type.type(:file).provide :windows do
resource.fail("Can only manage owner, group, and mode on filesystems that support Windows ACLs, such as NTFS")
end
end
+
+ attr_reader :file
+ private
+ def file
+ @file ||= Puppet::FileSystem::File.new(resource[:path])
+ end
end
diff --git a/lib/puppet/provider/group/aix.rb b/lib/puppet/provider/group/aix.rb
index 666748378..666748378 100755..100644
--- a/lib/puppet/provider/group/aix.rb
+++ b/lib/puppet/provider/group/aix.rb
diff --git a/lib/puppet/provider/group/windows_adsi.rb b/lib/puppet/provider/group/windows_adsi.rb
index 5811fc593..56c0c175b 100644
--- a/lib/puppet/provider/group/windows_adsi.rb
+++ b/lib/puppet/provider/group/windows_adsi.rb
@@ -1,13 +1,45 @@
require 'puppet/util/adsi'
Puppet::Type.type(:group).provide :windows_adsi do
- desc "Local group management for Windows. Nested groups are not supported."
+ desc "Local group management for Windows. Group members can be both users and groups.
+ Additionally, local groups can contain domain users."
defaultfor :operatingsystem => :windows
confine :operatingsystem => :windows
has_features :manages_members
+ def members_insync?(current, should)
+ return false unless current
+
+ # By comparing account SIDs we don't have to worry about case
+ # sensitivity, or canonicalization of account names.
+
+ # Cannot use munge of the group property to canonicalize @should
+ # since the default array_matching comparison is not commutative
+ should_empty = should.nil? or should.empty?
+
+ return false if current.empty? != should_empty
+
+ # dupes automatically weeded out when hashes built
+ Puppet::Util::ADSI::Group.name_sid_hash(current) == Puppet::Util::ADSI::Group.name_sid_hash(should)
+ end
+
+ def members_to_s(users)
+ return '' if users.nil? or !users.kind_of?(Array)
+ users = users.map do |user_name|
+ sid = Puppet::Util::Windows::Security.name_to_sid_object(user_name)
+ if sid.account =~ /\\/
+ account, _ = Puppet::Util::ADSI::User.parse_name(sid.account)
+ else
+ account = sid.account
+ end
+ resource.debug("#{sid.domain}\\#{account} (#{sid.to_s})")
+ "#{sid.domain}\\#{account}"
+ end
+ return users.join(',')
+ end
+
def group
@group ||= Puppet::Util::ADSI::Group.new(@resource[:name])
end
diff --git a/lib/puppet/provider/macauthorization/macauthorization.rb b/lib/puppet/provider/macauthorization/macauthorization.rb
index fe9c56985..e410d6f43 100644
--- a/lib/puppet/provider/macauthorization/macauthorization.rb
+++ b/lib/puppet/provider/macauthorization/macauthorization.rb
@@ -17,7 +17,7 @@ Puppet::Type.type(:macauthorization).provide :macauthorization, :parent => Puppe
# This should be confined based on macosx_productversion
# but puppet resource doesn't make the facts available and
# that interface is heavily used with this provider.
- if FileTest.exists?("/usr/bin/sw_vers")
+ if Puppet::FileSystem::File.exist?("/usr/bin/sw_vers")
product_version = sw_vers "-productVersion"
confine :true => unless /^10\.[0-4]/.match(product_version)
diff --git a/lib/puppet/provider/mailalias/aliases.rb b/lib/puppet/provider/mailalias/aliases.rb
index 87ee5b465..87ee5b465 100755..100644
--- a/lib/puppet/provider/mailalias/aliases.rb
+++ b/lib/puppet/provider/mailalias/aliases.rb
diff --git a/lib/puppet/provider/maillist/mailman.rb b/lib/puppet/provider/maillist/mailman.rb
index e070a25dd..e070a25dd 100755..100644
--- a/lib/puppet/provider/maillist/mailman.rb
+++ b/lib/puppet/provider/maillist/mailman.rb
diff --git a/lib/puppet/provider/mount/parsed.rb b/lib/puppet/provider/mount/parsed.rb
index d935d7d13..d935d7d13 100755..100644
--- a/lib/puppet/provider/mount/parsed.rb
+++ b/lib/puppet/provider/mount/parsed.rb
diff --git a/lib/puppet/provider/nameservice/directoryservice.rb b/lib/puppet/provider/nameservice/directoryservice.rb
index f0cbecea4..e8fe9016f 100644
--- a/lib/puppet/provider/nameservice/directoryservice.rb
+++ b/lib/puppet/provider/nameservice/directoryservice.rb
@@ -251,7 +251,7 @@ class Puppet::Provider::NameService::DirectoryService < Puppet::Provider::NameSe
Please check your password and try again.")
end
- if File.exists?("#{users_plist_dir}/#{resource_name}.plist")
+ if Puppet::FileSystem::File.exist?("#{users_plist_dir}/#{resource_name}.plist")
# If a plist already exists in /var/db/dslocal/nodes/Default/users, then
# we will need to extract the binary plist from the 'ShadowHashData'
# key, log the new password into the resultant plist's 'SALTED-SHA512'
@@ -296,7 +296,7 @@ class Puppet::Provider::NameService::DirectoryService < Puppet::Provider::NameSe
if (Puppet::Util::Package.versioncmp(get_macosx_version_major, '10.7') == -1)
password_hash = nil
password_hash_file = "#{password_hash_dir}/#{guid}"
- if File.exists?(password_hash_file) and File.file?(password_hash_file)
+ if Puppet::FileSystem::File.exist?(password_hash_file) and File.file?(password_hash_file)
fail("Could not read password hash file at #{password_hash_file}") if not File.readable?(password_hash_file)
f = File.new(password_hash_file)
password_hash = f.read
@@ -304,7 +304,7 @@ class Puppet::Provider::NameService::DirectoryService < Puppet::Provider::NameSe
end
password_hash
else
- if File.exists?("#{users_plist_dir}/#{username}.plist")
+ if Puppet::FileSystem::File.exist?("#{users_plist_dir}/#{username}.plist")
# If a plist exists in /var/db/dslocal/nodes/Default/users, we will
# extract the binary plist from the 'ShadowHashData' key, decode the
# salted-SHA512 password hash, and then return it.
diff --git a/lib/puppet/provider/package/appdmg.rb b/lib/puppet/provider/package/appdmg.rb
index 2910c8599..aaf5a7be2 100644
--- a/lib/puppet/provider/package/appdmg.rb
+++ b/lib/puppet/provider/package/appdmg.rb
@@ -93,7 +93,7 @@ Puppet::Type.type(:package).provide(:appdmg, :parent => Puppet::Provider::Packag
end
def query
- FileTest.exists?("/var/db/.puppet_appdmg_installed_#{@resource[:name]}") ? {:name => @resource[:name], :ensure => :present} : nil
+ Puppet::FileSystem::File.exist?("/var/db/.puppet_appdmg_installed_#{@resource[:name]}") ? {:name => @resource[:name], :ensure => :present} : nil
end
def install
diff --git a/lib/puppet/provider/package/apple.rb b/lib/puppet/provider/package/apple.rb
index 362ba60c7..a603d0783 100755..100644
--- a/lib/puppet/provider/package/apple.rb
+++ b/lib/puppet/provider/package/apple.rb
@@ -33,7 +33,7 @@ Puppet::Type.type(:package).provide :apple, :parent => Puppet::Provider::Package
end
def query
- FileTest.exists?("/Library/Receipts/#{@resource[:name]}.pkg") ? {:name => @resource[:name], :ensure => :present} : nil
+ Puppet::FileSystem::File.exist?("/Library/Receipts/#{@resource[:name]}.pkg") ? {:name => @resource[:name], :ensure => :present} : nil
end
def install
diff --git a/lib/puppet/provider/package/apt.rb b/lib/puppet/provider/package/apt.rb
index 16618d359..df5cd725a 100755..100644
--- a/lib/puppet/provider/package/apt.rb
+++ b/lib/puppet/provider/package/apt.rb
@@ -84,7 +84,7 @@ Puppet::Type.type(:package).provide :apt, :parent => :dpkg, :source => :dpkg do
# preseeds answers to dpkg-set-selection from the "responsefile"
#
def run_preseed
- if response = @resource[:responsefile] and FileTest.exist?(response)
+ if response = @resource[:responsefile] and Puppet::FileSystem::File.exist?(response)
self.info("Preseeding #{response} to debconf-set-selections")
preseed response
diff --git a/lib/puppet/provider/package/aptitude.rb b/lib/puppet/provider/package/aptitude.rb
index d3ab0da52..d3ab0da52 100755..100644
--- a/lib/puppet/provider/package/aptitude.rb
+++ b/lib/puppet/provider/package/aptitude.rb
diff --git a/lib/puppet/provider/package/blastwave.rb b/lib/puppet/provider/package/blastwave.rb
index 4200e5b2b..fc0698e1a 100755..100644
--- a/lib/puppet/provider/package/blastwave.rb
+++ b/lib/puppet/provider/package/blastwave.rb
@@ -18,7 +18,7 @@ Puppet::Type.type(:package).provide :blastwave, :parent => :sun, :source => :sun
"The pkg-get command is missing; blastwave packaging unavailable"
end
- unless FileTest.exists?("/var/pkg-get/admin")
+ unless Puppet::FileSystem::File.exist?("/var/pkg-get/admin")
Puppet.notice "It is highly recommended you create '/var/pkg-get/admin'."
Puppet.notice "See /var/pkg-get/admin-fullauto"
end
diff --git a/lib/puppet/provider/package/dpkg.rb b/lib/puppet/provider/package/dpkg.rb
index c8528cc4b..b65ef07bf 100755..100644
--- a/lib/puppet/provider/package/dpkg.rb
+++ b/lib/puppet/provider/package/dpkg.rb
@@ -40,7 +40,7 @@ Puppet::Type.type(:package).provide :dpkg, :parent => Puppet::Provider::Package
private
# Note: self:: is required here to keep these constants in the context of what will
- # eventually become this Puppet:Type::Package::ProviderDpkg class.
+ # eventually become this Puppet::Type::Package::ProviderDpkg class.
self::DPKG_DESCRIPTION_DELIMITER = ':DESC:'
self::DPKG_QUERY_FORMAT_STRING = %Q{'${Status} ${Package} ${Version} #{self::DPKG_DESCRIPTION_DELIMITER} ${Description}\\n#{self::DPKG_DESCRIPTION_DELIMITER}\\n'}
self::FIELDS_REGEX = %r{^(\S+) +(\S+) +(\S+) (\S+) (\S*) #{self::DPKG_DESCRIPTION_DELIMITER} (.*)$}
diff --git a/lib/puppet/provider/package/fink.rb b/lib/puppet/provider/package/fink.rb
index 7efa131b9..cbf141462 100755..100644
--- a/lib/puppet/provider/package/fink.rb
+++ b/lib/puppet/provider/package/fink.rb
@@ -56,7 +56,7 @@ Puppet::Type.type(:package).provide :fink, :parent => :dpkg, :source => :dpkg do
# preseeds answers to dpkg-set-selection from the "responsefile"
#
def run_preseed
- if response = @resource[:responsefile] and FileTest.exists?(response)
+ if response = @resource[:responsefile] and Puppet::FileSystem::File.exist?(response)
self.info("Preseeding #{response} to debconf-set-selections")
preseed response
diff --git a/lib/puppet/provider/package/freebsd.rb b/lib/puppet/provider/package/freebsd.rb
index adb72c607..adb72c607 100755..100644
--- a/lib/puppet/provider/package/freebsd.rb
+++ b/lib/puppet/provider/package/freebsd.rb
diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb
index a13bbf35f..a13bbf35f 100755..100644
--- a/lib/puppet/provider/package/gem.rb
+++ b/lib/puppet/provider/package/gem.rb
diff --git a/lib/puppet/provider/package/macports.rb b/lib/puppet/provider/package/macports.rb
index b410d6fc3..b410d6fc3 100755..100644
--- a/lib/puppet/provider/package/macports.rb
+++ b/lib/puppet/provider/package/macports.rb
diff --git a/lib/puppet/provider/package/msi.rb b/lib/puppet/provider/package/msi.rb
index 34da206cc..46617f13f 100644
--- a/lib/puppet/provider/package/msi.rb
+++ b/lib/puppet/provider/package/msi.rb
@@ -74,22 +74,18 @@ Puppet::Type.type(:package).provide(:msi, :parent => Puppet::Provider::Package)
# because of the special quoting we need to do around the MSI
# properties to use.
command = ['msiexec.exe', '/qn', '/norestart', '/i', shell_quote(resource[:source]), install_options].flatten.compact.join(' ')
- execute(command, :failonfail => false, :combine => true)
+ output = execute(command, :failonfail => false, :combine => true)
- check_result(exit_status)
+ check_result(output.exitstatus)
end
def uninstall
fail("The productcode property is missing.") unless properties[:productcode]
command = ['msiexec.exe', '/qn', '/norestart', '/x', properties[:productcode], uninstall_options].flatten.compact.join(' ')
- execute(command, :failonfail => false, :combine => true)
+ output = execute(command, :failonfail => false, :combine => true)
- check_result(exit_status)
- end
-
- def exit_status
- $CHILD_STATUS.exitstatus
+ check_result(output.exitstatus)
end
# (Un)install may "fail" because the package requested a reboot, the system requested a
@@ -102,8 +98,6 @@ Puppet::Type.type(:package).provide(:msi, :parent => Puppet::Provider::Package)
case hr
when 0
# yeah
- when 194
- warning("The package requested a reboot to finish the operation.")
when 1641
warning("The package #{operation}ed successfully and the system is rebooting now.")
when 3010
diff --git a/lib/puppet/provider/package/nim.rb b/lib/puppet/provider/package/nim.rb
index 61d4cbcbf..1869cf57f 100644
--- a/lib/puppet/provider/package/nim.rb
+++ b/lib/puppet/provider/package/nim.rb
@@ -151,10 +151,10 @@ Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do
# I spent a lot of time trying to figure out a solution that didn't
# require parsing the `nimclient -o showres` output and was unable to
# do so.
- HEADER_LINE_REGEX = /^([^\s]+)\s+[^@]+@@(I|R):(\1)\s+[^\s]+$/
- PACKAGE_LINE_REGEX = /^.*@@(I|R):(.*)$/
- RPM_PACKAGE_REGEX = /^(.*)-(.*-\d+) \2$/
- INSTALLP_PACKAGE_REGEX = /^(.*) (.*)$/
+ self::HEADER_LINE_REGEX = /^([^\s]+)\s+[^@]+@@(I|R):(\1)\s+[^\s]+$/
+ self::PACKAGE_LINE_REGEX = /^.*@@(I|R):(.*)$/
+ self::RPM_PACKAGE_REGEX = /^(.*)-(.*-\d+) \2$/
+ self::INSTALLP_PACKAGE_REGEX = /^(.*) (.*)$/
# Here is some sample output that shows what the above regexes will be up
# against:
@@ -205,13 +205,13 @@ Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do
# meant to validate that the header line for the package listing output
# looks sane, so we know we're dealing with the kind of output that we
# are capable of handling.
- unless line.match(HEADER_LINE_REGEX)
+ unless line.match(self.class::HEADER_LINE_REGEX)
self.fail "Unable to parse output from nimclient showres: line does not match expected package header format:\n'#{line}'"
end
end
def parse_installp_package_string(package_string)
- unless match = package_string.match(INSTALLP_PACKAGE_REGEX)
+ unless match = package_string.match(self.class::INSTALLP_PACKAGE_REGEX)
self.fail "Unable to parse output from nimclient showres: package string does not match expected installp package string format:\n'#{package_string}'"
end
package_name = match.captures[0]
@@ -220,7 +220,7 @@ Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do
end
def parse_rpm_package_string(package_string)
- unless match = package_string.match(RPM_PACKAGE_REGEX)
+ unless match = package_string.match(self.class::RPM_PACKAGE_REGEX)
self.fail "Unable to parse output from nimclient showres: package string does not match expected rpm package string format:\n'#{package_string}'"
end
package_name = match.captures[0]
@@ -229,7 +229,7 @@ Puppet::Type.type(:package).provide :nim, :parent => :aix, :source => :aix do
end
def parse_showres_package_line(line)
- unless match = line.match(PACKAGE_LINE_REGEX)
+ unless match = line.match(self.class::PACKAGE_LINE_REGEX)
self.fail "Unable to parse output from nimclient showres: line does not match expected package line format:\n'#{line}'"
end
diff --git a/lib/puppet/provider/package/openbsd.rb b/lib/puppet/provider/package/openbsd.rb
index 75c01ec83..d51bc8f04 100755..100644
--- a/lib/puppet/provider/package/openbsd.rb
+++ b/lib/puppet/provider/package/openbsd.rb
@@ -54,7 +54,7 @@ Puppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Packa
def parse_pkgconf
unless @resource[:source]
- if File.exist?("/etc/pkg.conf")
+ if Puppet::FileSystem::File.exist?("/etc/pkg.conf")
File.open("/etc/pkg.conf", "rb").readlines.each do |line|
if matchdata = line.match(/^installpath\s*=\s*(.+)\s*$/i)
@resource[:source] = matchdata[1]
diff --git a/lib/puppet/provider/package/opkg.rb b/lib/puppet/provider/package/opkg.rb
index 439afeb01..439afeb01 100755..100644
--- a/lib/puppet/provider/package/opkg.rb
+++ b/lib/puppet/provider/package/opkg.rb
diff --git a/lib/puppet/provider/package/pacman.rb b/lib/puppet/provider/package/pacman.rb
index 0b47a4e8b..f811aa5a8 100644
--- a/lib/puppet/provider/package/pacman.rb
+++ b/lib/puppet/provider/package/pacman.rb
@@ -6,7 +6,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag
commands :pacman => "/usr/bin/pacman"
# Yaourt is a common AUR helper which, if installed, we can use to query the AUR
- commands :yaourt => "/usr/bin/yaourt" if File.exists? '/usr/bin/yaourt'
+ commands :yaourt => "/usr/bin/yaourt" if Puppet::FileSystem::File.exist? '/usr/bin/yaourt'
confine :operatingsystem => :archlinux
defaultfor :operatingsystem => :archlinux
@@ -14,7 +14,7 @@ Puppet::Type.type(:package).provide :pacman, :parent => Puppet::Provider::Packag
# If yaourt is installed, we can make use of it
def yaourt?
- return File.exists? '/usr/bin/yaourt'
+ return Puppet::FileSystem::File.exist? '/usr/bin/yaourt'
end
# Install a package using 'pacman', or 'yaourt' if available.
diff --git a/lib/puppet/provider/package/pkgdmg.rb b/lib/puppet/provider/package/pkgdmg.rb
index 15c3639de..f14b53158 100644
--- a/lib/puppet/provider/package/pkgdmg.rb
+++ b/lib/puppet/provider/package/pkgdmg.rb
@@ -113,7 +113,7 @@ Puppet::Type.type(:package).provide :pkgdmg, :parent => Puppet::Provider::Packag
end
def query
- if FileTest.exists?("/var/db/.puppet_pkgdmg_installed_#{@resource[:name]}")
+ if Puppet::FileSystem::File.exist?("/var/db/.puppet_pkgdmg_installed_#{@resource[:name]}")
Puppet.debug "/var/db/.puppet_pkgdmg_installed_#{@resource[:name]} found"
return {:name => @resource[:name], :ensure => :present}
else
diff --git a/lib/puppet/provider/package/pkgutil.rb b/lib/puppet/provider/package/pkgutil.rb
index 157066415..c114fa949 100755..100644
--- a/lib/puppet/provider/package/pkgutil.rb
+++ b/lib/puppet/provider/package/pkgutil.rb
@@ -14,7 +14,7 @@ Puppet::Type.type(:package).provide :pkgutil, :parent => :sun, :source => :sun d
end
def self.healthcheck()
- unless FileTest.exists?("/var/opt/csw/pkgutil/admin")
+ unless Puppet::FileSystem::File.exist?("/var/opt/csw/pkgutil/admin")
Puppet.notice "It is highly recommended you create '/var/opt/csw/pkgutil/admin'."
Puppet.notice "See /var/opt/csw/pkgutil"
end
diff --git a/lib/puppet/provider/package/ports.rb b/lib/puppet/provider/package/ports.rb
index 9141e30f5..9141e30f5 100755..100644
--- a/lib/puppet/provider/package/ports.rb
+++ b/lib/puppet/provider/package/ports.rb
diff --git a/lib/puppet/provider/package/rpm.rb b/lib/puppet/provider/package/rpm.rb
index e99e51c7b..6b89eaa62 100755..100644
--- a/lib/puppet/provider/package/rpm.rb
+++ b/lib/puppet/provider/package/rpm.rb
@@ -3,15 +3,22 @@ require 'puppet/provider/package'
# RPM packaging. Should work anywhere that has rpm installed.
Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Provider::Package do
desc "RPM packaging support; should work anywhere with a working `rpm`
- binary."
+ binary.
+
+ This provider supports the `install_options` attribute, which allows
+ command-line flags to be passed to the RPM binary. Install options should be
+ specified as an array, where each element is either a string or a
+ `{'--flag' => 'value'}` hash. (That hash example would be equivalent to a
+ `'--flag=value'` string; the hash syntax is available as a convenience.)"
has_feature :versionable
+ has_feature :install_options
# Note: self:: is required here to keep these constants in the context of what will
- # eventually become this Puppet:Type::Package::ProviderRpm class.
+ # eventually become this Puppet::Type::Package::ProviderRpm class.
self::RPM_DESCRIPTION_DELIMITER = ':DESC:'
# The query format by which we identify installed packages
- self::NEVRA_FORMAT = %Q{'%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH} #{self::RPM_DESCRIPTION_DELIMITER} %{SUMMARY}\\n'}
+ self::NEVRA_FORMAT = %Q{%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH} #{self::RPM_DESCRIPTION_DELIMITER} %{SUMMARY}\\n}
self::NEVRA_REGEX = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)(?: #{self::RPM_DESCRIPTION_DELIMITER} ?(.*))?$}
self::RPM_PACKAGE_NOT_FOUND_REGEX = /package .+ is not installed/
self::NEVRA_FIELDS = [:name, :epoch, :version, :release, :arch, :description]
@@ -49,7 +56,7 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
# list out all of the packages
begin
- execpipe("#{command(:rpm)} -qa #{nosignature} #{nodigest} --qf #{self::NEVRA_FORMAT}") { |process|
+ execpipe("#{command(:rpm)} -qa #{nosignature} #{nodigest} --qf '#{self::NEVRA_FORMAT}'") { |process|
# now turn each returned line into a package object
process.each_line { |line|
hash = nevra_to_hash(line)
@@ -107,9 +114,10 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
return
end
- flag = "-i"
+ flag = ["-i"]
flag = ["-U", "--oldpackage"] if @property_hash[:ensure] and @property_hash[:ensure] != :absent
+ flag = flag + install_options
rpm flag, source
end
@@ -139,8 +147,36 @@ Puppet::Type.type(:package).provide :rpm, :source => :rpm, :parent => Puppet::Pr
self.install
end
+ def install_options
+ join_options(resource[:install_options])
+ end
+
private
+ # Turns a array of options into flags to be passed to rpm install(8) and
+ # The options can be passed as a string or hash. Note that passing a hash
+ # should only be used in case -Dfoo=bar must be passed,
+ # which can be accomplished with:
+ # install_options => [ { '-Dfoo' => 'bar' } ]
+ # Regular flags like '-L' must be passed as a string.
+ # @param options [Array]
+ # @return Concatenated list of options
+ # @api private
+ def join_options(options)
+ return [] unless options
+
+ options.collect do |val|
+ case val
+ when Hash
+ val.keys.sort.collect do |k|
+ "#{k}=#{val[k]}"
+ end.join(' ')
+ else
+ val
+ end
+ end
+ end
+
# @param line [String] one line of rpm package query information
# @return [Hash] of NEVRA_FIELDS strings parsed from package info
# if we failed to parse
diff --git a/lib/puppet/provider/package/sun.rb b/lib/puppet/provider/package/sun.rb
index 5f15ad13e..87a13a2a5 100755..100644
--- a/lib/puppet/provider/package/sun.rb
+++ b/lib/puppet/provider/package/sun.rb
@@ -15,7 +15,7 @@ Puppet::Type.type(:package).provide :sun, :parent => Puppet::Provider::Package d
has_feature :install_options
- Namemap = {
+ self::Namemap = {
"PKGINST" => :name,
"CATEGORY" => :category,
"ARCH" => :platform,
@@ -26,8 +26,8 @@ Puppet::Type.type(:package).provide :sun, :parent => Puppet::Provider::Package d
}
def self.namemap(hash)
- Namemap.keys.inject({}) do |hsh,k|
- hsh.merge(Namemap[k] => hash[k])
+ self::Namemap.keys.inject({}) do |hsh,k|
+ hsh.merge(self::Namemap[k] => hash[k])
end
end
diff --git a/lib/puppet/provider/package/sunfreeware.rb b/lib/puppet/provider/package/sunfreeware.rb
index 118d36b66..118d36b66 100755..100644
--- a/lib/puppet/provider/package/sunfreeware.rb
+++ b/lib/puppet/provider/package/sunfreeware.rb
diff --git a/lib/puppet/provider/package/windows.rb b/lib/puppet/provider/package/windows.rb
index 7ca022c8e..d4936c89b 100644
--- a/lib/puppet/provider/package/windows.rb
+++ b/lib/puppet/provider/package/windows.rb
@@ -22,6 +22,7 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
has_feature :uninstallable
has_feature :install_options
has_feature :uninstall_options
+ has_feature :versionable
attr_accessor :package
@@ -37,9 +38,7 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
def self.to_hash(pkg)
{
:name => pkg.name,
- # we're not versionable, so we can't set the ensure
- # parameter to the currently installed version
- :ensure => :installed,
+ :ensure => pkg.version || :installed,
:provider => :windows
}
end
@@ -59,26 +58,22 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
installer = Puppet::Provider::Package::Windows::Package.installer_class(resource)
command = [installer.install_command(resource), install_options].flatten.compact.join(' ')
- execute(command, :failonfail => false, :combine => true)
+ output = execute(command, :failonfail => false, :combine => true)
- check_result(exit_status)
+ check_result(output.exitstatus)
end
def uninstall
command = [package.uninstall_command, uninstall_options].flatten.compact.join(' ')
- execute(command, :failonfail => false, :combine => true)
+ output = execute(command, :failonfail => false, :combine => true)
- check_result(exit_status)
- end
-
- def exit_status
- $CHILD_STATUS.exitstatus
+ check_result(output.exitstatus)
end
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa368542(v=vs.85).aspx
- ERROR_SUCCESS = 0
- ERROR_SUCCESS_REBOOT_INITIATED = 1641
- ERROR_SUCCESS_REBOOT_REQUIRED = 3010
+ self::ERROR_SUCCESS = 0
+ self::ERROR_SUCCESS_REBOOT_INITIATED = 1641
+ self::ERROR_SUCCESS_REBOOT_REQUIRED = 3010
# (Un)install may "fail" because the package requested a reboot, the system requested a
# reboot, or something else entirely. Reboot requests mean the package was installed
@@ -87,13 +82,11 @@ Puppet::Type.type(:package).provide(:windows, :parent => Puppet::Provider::Packa
operation = resource[:ensure] == :absent ? 'uninstall' : 'install'
case hr
- when ERROR_SUCCESS
+ when self.class::ERROR_SUCCESS
# yeah
- when 194
- warning("The package requested a reboot to finish the operation.")
- when ERROR_SUCCESS_REBOOT_INITIATED
+ when self.class::ERROR_SUCCESS_REBOOT_INITIATED
warning("The package #{operation}ed successfully and the system is rebooting now.")
- when ERROR_SUCCESS_REBOOT_REQUIRED
+ when self.class::ERROR_SUCCESS_REBOOT_REQUIRED
warning("The package #{operation}ed successfully, but the system must be rebooted.")
else
raise Puppet::Util::Windows::Error.new("Failed to #{operation}", hr)
diff --git a/lib/puppet/provider/package/windows/package.rb b/lib/puppet/provider/package/windows/package.rb
index fd007de6c..e26c6773c 100644
--- a/lib/puppet/provider/package/windows/package.rb
+++ b/lib/puppet/provider/package/windows/package.rb
@@ -58,7 +58,7 @@ class Puppet::Provider::Package::Windows
# REMIND: what about msp, etc
MsiPackage
when /\.exe"?\Z/i
- fail("The source does not exist: '#{resource[:source]}'") unless File.exists?(resource[:source])
+ fail("The source does not exist: '#{resource[:source]}'") unless Puppet::FileSystem::File.exist?(resource[:source])
ExePackage
else
fail("Don't know how to install '#{resource[:source]}'")
diff --git a/lib/puppet/provider/package/yum.rb b/lib/puppet/provider/package/yum.rb
index dd41aa470..c32052d3d 100644
--- a/lib/puppet/provider/package/yum.rb
+++ b/lib/puppet/provider/package/yum.rb
@@ -11,7 +11,7 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
commands :yum => "yum", :rpm => "rpm", :python => "python"
- YUMHELPER = File::join(File::dirname(__FILE__), "yumhelper.py")
+ self::YUMHELPER = File::join(File::dirname(__FILE__), "yumhelper.py")
attr_accessor :latest_info
@@ -34,7 +34,7 @@ Puppet::Type.type(:package).provide :yum, :parent => :rpm, :source => :rpm do
# collect our 'latest' info
updates = {}
- python(YUMHELPER).each_line do |l|
+ python(self::YUMHELPER).each_line do |l|
l.chomp!
next if l.empty?
if l[0,4] == "_pkg"
diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb
index 1a13c8df3..1a13c8df3 100755..100644
--- a/lib/puppet/provider/parsedfile.rb
+++ b/lib/puppet/provider/parsedfile.rb
diff --git a/lib/puppet/provider/port/parsed.rb b/lib/puppet/provider/port/parsed.rb
index 5c973b6af..5c973b6af 100755..100644
--- a/lib/puppet/provider/port/parsed.rb
+++ b/lib/puppet/provider/port/parsed.rb
diff --git a/lib/puppet/provider/service/base.rb b/lib/puppet/provider/service/base.rb
index 0d960854f..0d960854f 100755..100644
--- a/lib/puppet/provider/service/base.rb
+++ b/lib/puppet/provider/service/base.rb
diff --git a/lib/puppet/provider/service/bsd.rb b/lib/puppet/provider/service/bsd.rb
index 85e7824fa..a19608a51 100644
--- a/lib/puppet/provider/service/bsd.rb
+++ b/lib/puppet/provider/service/bsd.rb
@@ -20,13 +20,13 @@ Puppet::Type.type(:service).provide :bsd, :parent => :init do
# remove service file from rc.conf.d to disable it
def disable
rcfile = File.join(rcconf_dir, @model[:name])
- File.delete(rcfile) if File.exists?(rcfile)
+ File.delete(rcfile) if Puppet::FileSystem::File.exist?(rcfile)
end
# if the service file exists in rc.conf.d then it's already enabled
def enabled?
rcfile = File.join(rcconf_dir, @model[:name])
- return :true if File.exists?(rcfile)
+ return :true if Puppet::FileSystem::File.exist?(rcfile)
:false
end
@@ -34,7 +34,7 @@ Puppet::Type.type(:service).provide :bsd, :parent => :init do
# enable service by creating a service file under rc.conf.d with the
# proper contents
def enable
- Dir.mkdir(rcconf_dir) if not File.exists?(rcconf_dir)
+ Dir.mkdir(rcconf_dir) if not Puppet::FileSystem::File.exist?(rcconf_dir)
rcfile = File.join(rcconf_dir, @model[:name])
open(rcfile, 'w') { |f| f << "%s_enable=\"YES\"\n" % @model[:name] }
end
diff --git a/lib/puppet/provider/service/daemontools.rb b/lib/puppet/provider/service/daemontools.rb
index f7784cc61..32c0e0ff7 100644
--- a/lib/puppet/provider/service/daemontools.rb
+++ b/lib/puppet/provider/service/daemontools.rb
@@ -48,7 +48,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do
def defpath(dummy_argument=:work_arround_for_ruby_GC_bug)
unless @defpath
["/var/lib/service", "/etc"].each do |path|
- if FileTest.exist?(path)
+ if Puppet::FileSystem::File.exist?(path)
@defpath = path
break
end
@@ -74,7 +74,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do
# or don't contain a run file
Dir.entries(path).reject { |e|
fullpath = File.join(path, e)
- e =~ /^\./ or ! FileTest.directory?(fullpath) or ! FileTest.exist?(File.join(fullpath,"run"))
+ e =~ /^\./ or ! FileTest.directory?(fullpath) or ! Puppet::FileSystem::File.exist?(File.join(fullpath,"run"))
}.collect do |name|
new(:name => name, :path => path)
end
@@ -89,7 +89,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do
def servicedir
unless @servicedir
["/service", "/etc/service","/var/lib/svscan"].each do |path|
- if FileTest.exist?(path)
+ if Puppet::FileSystem::File.exist?(path)
@servicedir = path
break
end
@@ -142,7 +142,7 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do
return :true
else
# the service is enabled if it is linked
- return FileTest.symlink?(self.service) ? :true : :false
+ return Puppet::FileSystem::File.new(self.service).symlink? ? :true : :false
end
end
@@ -152,9 +152,9 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do
self.setupservice
end
if self.daemon
- if ! FileTest.symlink?(self.service)
+ if ! Puppet::FileSystem::File.new(self.service).symlink?
Puppet.notice "Enabling #{self.service}: linking #{self.daemon} -> #{self.service}"
- File.symlink(self.daemon, self.service)
+ Puppet::FileSystem::File.new(self.daemon).symlink(self.service)
end
end
rescue Puppet::ExecutionFailure
@@ -168,9 +168,9 @@ Puppet::Type.type(:service).provide :daemontools, :parent => :base do
self.setupservice
end
if self.daemon
- if FileTest.symlink?(self.service)
+ if Puppet::FileSystem::File.new(self.service).symlink?
Puppet.notice "Disabling #{self.service}: removing link #{self.daemon} -> #{self.service}"
- File.unlink(self.service)
+ Puppet::FileSystem::File.unlink(self.service)
end
end
rescue Puppet::ExecutionFailure
diff --git a/lib/puppet/provider/service/debian.rb b/lib/puppet/provider/service/debian.rb
index 7d14eaa3c..7d14eaa3c 100755..100644
--- a/lib/puppet/provider/service/debian.rb
+++ b/lib/puppet/provider/service/debian.rb
diff --git a/lib/puppet/provider/service/freebsd.rb b/lib/puppet/provider/service/freebsd.rb
index 8cf07bab5..36de850c5 100644
--- a/lib/puppet/provider/service/freebsd.rb
+++ b/lib/puppet/provider/service/freebsd.rb
@@ -71,7 +71,7 @@ Puppet::Type.type(:service).provide :freebsd, :parent => :init do
success = false
# Replace in all files, not just in the first found with a match
[rcconf, rcconf_local, rcconf_dir + "/#{service}"].each do |filename|
- if File.exists?(filename)
+ if Puppet::FileSystem::File.exist?(filename)
s = File.read(filename)
if s.gsub!(/^(#{rcvar}(_enable)?)=\"?(YES|NO)\"?/, "\\1=\"#{yesno}\"")
File.open(filename, File::WRONLY) { |f| f << s }
@@ -87,14 +87,14 @@ Puppet::Type.type(:service).provide :freebsd, :parent => :init do
def rc_add(service, rcvar, yesno)
append = "\# Added by Puppet\n#{rcvar}_enable=\"#{yesno}\"\n"
# First, try the one-file-per-service style
- if File.exists?(rcconf_dir)
+ if Puppet::FileSystem::File.exist?(rcconf_dir)
File.open(rcconf_dir + "/#{service}", File::WRONLY | File::APPEND | File::CREAT, 0644) {
|f| f << append
self.debug("Appended to #{f.path}")
}
else
# Else, check the local rc file first, but don't create it
- if File.exists?(rcconf_local)
+ if Puppet::FileSystem::File.exist?(rcconf_local)
File.open(rcconf_local, File::WRONLY | File::APPEND) {
|f| f << append
self.debug("Appended to #{f.path}")
diff --git a/lib/puppet/provider/service/init.rb b/lib/puppet/provider/service/init.rb
index 23ad824b2..d46cc8772 100755..100644
--- a/lib/puppet/provider/service/init.rb
+++ b/lib/puppet/provider/service/init.rb
@@ -99,7 +99,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
if File.directory?(path)
true
else
- if File.exist?(path)
+ if Puppet::FileSystem::File.exist?(path)
self.debug "Search path #{path} is not a directory"
else
self.debug "Search path #{path} does not exist"
@@ -112,7 +112,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
def search(name)
paths.each do |path|
fqname = File.join(path,name)
- if File.exist? fqname
+ if Puppet::FileSystem::File.exist? fqname
return fqname
else
self.debug("Could not find #{name} in #{path}")
@@ -121,7 +121,7 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
paths.each do |path|
fqname_sh = File.join(path,"#{name}.sh")
- if File.exist? fqname_sh
+ if Puppet::FileSystem::File.exist? fqname_sh
return fqname_sh
else
self.debug("Could not find #{name}.sh in #{path}")
@@ -154,7 +154,8 @@ Puppet::Type.type(:service).provide :init, :parent => :base do
private
def self.is_init?(script = initscript)
- !File.symlink?(script) || File.readlink(script) != "/lib/init/upstart-job"
+ file = Puppet::FileSystem::File.new(script)
+ !file.symlink? || file.readlink != "/lib/init/upstart-job"
end
end
diff --git a/lib/puppet/provider/service/launchd.rb b/lib/puppet/provider/service/launchd.rb
index 206676aec..316fdbf46 100644
--- a/lib/puppet/provider/service/launchd.rb
+++ b/lib/puppet/provider/service/launchd.rb
@@ -112,40 +112,51 @@ Puppet::Type.type(:service).provide :launchd, :parent => :base do
array_of_files.compact
end
+ # Get a hash of all launchd plists, keyed by label. This value is cached, but
+ # the cache will be refreshed if refresh is true.
+ #
+ # @api private
+ def self.make_label_to_path_map(refresh=false)
+ return @label_to_path_map if @label_to_path_map and not refresh
+ @label_to_path_map = {}
+ launchd_paths.each do |path|
+ return_globbed_list_of_file_paths(path).each do |filepath|
+ job = read_plist(filepath)
+ next if job.nil?
+ if job.has_key?("Label")
+ @label_to_path_map[job["Label"]] = filepath
+ else
+ Puppet.warning("The #{filepath} plist does not contain a 'label' key; " +
+ "Puppet is skipping it")
+ next
+ end
+ end
+ end
+ @label_to_path_map
+ end
+
# Sets a class instance variable with a hash of all launchd plist files that
# are found on the system. The key of the hash is the job id and the value
# is the path to the file. If a label is passed, we return the job id and
# path for that specific job.
def self.jobsearch(label=nil)
- @label_to_path_map ||= {}
- if @label_to_path_map.empty?
- launchd_paths.each do |path|
- return_globbed_list_of_file_paths(path).each do |filepath|
- job = read_plist(filepath)
- next if job.nil?
- if job.has_key?("Label")
- if job["Label"] == label
- return { label => filepath }
- else
- @label_to_path_map[job["Label"]] = filepath
- end
- else
- Puppet.warning("The #{filepath} plist does not contain a 'label' key; " +
- "Puppet is skipping it")
- next
- end
- end
- end
- end
+ by_label = make_label_to_path_map
if label
- if @label_to_path_map.has_key? label
- return { label => @label_to_path_map[label] }
+ if by_label.has_key? label
+ return { label => by_label[label] }
else
- raise Puppet::Error.new("Unable to find launchd plist for job: #{label}")
+ # try refreshing the map, in case a plist has been added in the interim
+ by_label = make_label_to_path_map(true)
+ if by_label.has_key? label
+ return { label => by_label[label] }
+ else
+ raise Puppet::Error, "Unable to find launchd plist for job: #{label}"
+ end
end
else
- @label_to_path_map
+ # caller wants the whole map
+ by_label
end
end
diff --git a/lib/puppet/provider/service/openbsd.rb b/lib/puppet/provider/service/openbsd.rb
new file mode 100644
index 000000000..8fe68955e
--- /dev/null
+++ b/lib/puppet/provider/service/openbsd.rb
@@ -0,0 +1,23 @@
+Puppet::Type.type(:service).provide :openbsd, :parent => :init do
+
+ desc "Provider for OpenBSD's rc.d daemon control scripts"
+
+ confine :operatingsystem => :openbsd
+ defaultfor :operatingsystem => :openbsd
+
+ def self.defpath
+ ["/etc/rc.d"]
+ end
+
+ def startcmd
+ [self.initscript, "-f", :start]
+ end
+
+ def restartcmd
+ (@resource[:hasrestart] == :true) && [self.initscript, "-f", :restart]
+ end
+
+ def statuscmd
+ [self.initscript, :check]
+ end
+end
diff --git a/lib/puppet/provider/service/redhat.rb b/lib/puppet/provider/service/redhat.rb
index c1c6401c1..c1c6401c1 100755..100644
--- a/lib/puppet/provider/service/redhat.rb
+++ b/lib/puppet/provider/service/redhat.rb
diff --git a/lib/puppet/provider/service/runit.rb b/lib/puppet/provider/service/runit.rb
index d326b1ebf..8d835d2db 100644
--- a/lib/puppet/provider/service/runit.rb
+++ b/lib/puppet/provider/service/runit.rb
@@ -42,7 +42,7 @@ Puppet::Type.type(:service).provide :runit, :parent => :daemontools do
def defpath(dummy_argument=:work_arround_for_ruby_GC_bug)
unless @defpath
["/etc/sv", "/var/lib/service"].each do |path|
- if FileTest.exist?(path)
+ if Puppet::FileSystem::File.exist?(path)
@defpath = path
break
end
@@ -57,7 +57,7 @@ Puppet::Type.type(:service).provide :runit, :parent => :daemontools do
def servicedir
unless @servicedir
["/service", "/etc/service","/var/service"].each do |path|
- if FileTest.exist?(path)
+ if Puppet::FileSystem::File.exist?(path)
@servicedir = path
break
end
@@ -105,7 +105,7 @@ Puppet::Type.type(:service).provide :runit, :parent => :daemontools do
# before a disable
def disable
# unlink the daemon symlink to disable it
- File.unlink(self.service) if FileTest.symlink?(self.service)
+ Puppet::FileSystem::File.unlink(self.service) if Puppet::FileSystem::File.new(self.service).symlink?
end
end
diff --git a/lib/puppet/provider/service/smf.rb b/lib/puppet/provider/service/smf.rb
index 25ae551de..25ae551de 100755..100644
--- a/lib/puppet/provider/service/smf.rb
+++ b/lib/puppet/provider/service/smf.rb
diff --git a/lib/puppet/provider/service/src.rb b/lib/puppet/provider/service/src.rb
index 8028751ff..8028751ff 100755..100644
--- a/lib/puppet/provider/service/src.rb
+++ b/lib/puppet/provider/service/src.rb
diff --git a/lib/puppet/provider/service/systemd.rb b/lib/puppet/provider/service/systemd.rb
index 1ff070b86..1ff070b86 100755..100644
--- a/lib/puppet/provider/service/systemd.rb
+++ b/lib/puppet/provider/service/systemd.rb
diff --git a/lib/puppet/provider/service/upstart.rb b/lib/puppet/provider/service/upstart.rb
index 04665c971..c7fd58c4e 100755..100644
--- a/lib/puppet/provider/service/upstart.rb
+++ b/lib/puppet/provider/service/upstart.rb
@@ -71,7 +71,7 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do
paths.each do |path|
service_name = name.match(/^(\S+)/)[1]
fqname = File.join(path, service_name + suffix)
- if File.exists?(fqname)
+ if Puppet::FileSystem::File.exist?(fqname)
return fqname
end
@@ -148,7 +148,7 @@ Puppet::Type.type(:service).provide :upstart, :parent => :debian do
private
def is_upstart?(script = initscript)
- File.exists?(script) && script.match(/\/etc\/init\/\S+\.conf/)
+ Puppet::FileSystem::File.exist?(script) && script.match(/\/etc\/init\/\S+\.conf/)
end
def version_is_pre_0_6_7
@@ -256,7 +256,7 @@ private
end
def read_override_file
- if File.exists?(overscript)
+ if Puppet::FileSystem::File.exist?(overscript)
read_script_from(overscript)
else
""
diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb
index 44fef458e..cb08b593f 100644
--- a/lib/puppet/provider/ssh_authorized_key/parsed.rb
+++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb
@@ -41,7 +41,7 @@ Puppet::Type.type(:ssh_authorized_key).provide(
end
def user
- uid = File.stat(target).uid
+ uid = Puppet::FileSystem::File.new(target).stat.uid
Etc.getpwuid(uid).name
end
@@ -55,7 +55,7 @@ Puppet::Type.type(:ssh_authorized_key).provide(
self.class.backup_target(target)
Puppet::Util::SUIDManager.asuser(@resource.should(:user)) do
- unless File.exist?(dir = File.dirname(target))
+ unless Puppet::FileSystem::File.exist?(dir = File.dirname(target))
Puppet.debug "Creating #{dir}"
Dir.mkdir(dir, dir_perm)
end
diff --git a/lib/puppet/provider/sshkey/parsed.rb b/lib/puppet/provider/sshkey/parsed.rb
index f874683b7..f874683b7 100755..100644
--- a/lib/puppet/provider/sshkey/parsed.rb
+++ b/lib/puppet/provider/sshkey/parsed.rb
diff --git a/lib/puppet/provider/user/aix.rb b/lib/puppet/provider/user/aix.rb
index 0831f2e26..0831f2e26 100755..100644
--- a/lib/puppet/provider/user/aix.rb
+++ b/lib/puppet/provider/user/aix.rb
diff --git a/lib/puppet/provider/user/directoryservice.rb b/lib/puppet/provider/user/directoryservice.rb
index c9110808d..6a59c4bee 100644
--- a/lib/puppet/provider/user/directoryservice.rb
+++ b/lib/puppet/provider/user/directoryservice.rb
@@ -238,7 +238,7 @@ Puppet::Type.type(:user).provide :directoryservice do
def self.get_sha1(guid)
password_hash = nil
password_hash_file = "#{password_hash_dir}/#{guid}"
- if File.exists?(password_hash_file) and File.file?(password_hash_file)
+ if Puppet::FileSystem::File.exist?(password_hash_file) and File.file?(password_hash_file)
raise Puppet::Error, "Could not read password hash file at #{password_hash_file}" if not File.readable?(password_hash_file)
f = File.new(password_hash_file)
password_hash = f.read
diff --git a/lib/puppet/provider/user/useradd.rb b/lib/puppet/provider/user/useradd.rb
index 741d0c1d7..2c6552c73 100644
--- a/lib/puppet/provider/user/useradd.rb
+++ b/lib/puppet/provider/user/useradd.rb
@@ -160,7 +160,7 @@ Puppet::Type.type(:user).provide :useradd, :parent => Puppet::Provider::NameServ
else
cmd = [command(:add)]
end
- if not @resource.should(gid) and Puppet::Util.gid(@resource[:name])
+ if not @resource.should(:gid) and Puppet::Util.gid(@resource[:name])
cmd += ["-g", @resource[:name]]
end
cmd += add_properties
diff --git a/lib/puppet/provider/zone/solaris.rb b/lib/puppet/provider/zone/solaris.rb
index 6e0d14c31..2cd0e99bb 100644
--- a/lib/puppet/provider/zone/solaris.rb
+++ b/lib/puppet/provider/zone/solaris.rb
@@ -261,7 +261,7 @@ Puppet::Type.type(:zone).provide(:solaris) do
# which makes zoneadmd mount the zone root
zoneadm :ready unless File.directory?(zoneetc)
- unless File.exists?(sysidcfg)
+ unless Puppet::FileSystem::File.exist?(sysidcfg)
begin
File.open(sysidcfg, "w", 0600) do |f|
f.puts cfg
diff --git a/lib/puppet/rails/benchmark.rb b/lib/puppet/rails/benchmark.rb
index 741b6d5bd..8d88280c3 100644
--- a/lib/puppet/rails/benchmark.rb
+++ b/lib/puppet/rails/benchmark.rb
@@ -52,7 +52,7 @@ module Puppet::Rails::Benchmark
file = "/tmp/time_debugging.yaml"
- if FileTest.exist?(file)
+ if Puppet::FileSystem::File.exist?(file)
data = YAML.load_file(file)
else
data = {}
diff --git a/lib/puppet/reference/configuration.rb b/lib/puppet/reference/configuration.rb
index e7f495403..c1580fe35 100644
--- a/lib/puppet/reference/configuration.rb
+++ b/lib/puppet/reference/configuration.rb
@@ -14,14 +14,13 @@ config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc
# Print the doc string itself
begin
- str << object.desc.gsub(/\n/, " ")
+ str << Puppet::Util::Docs.scrub(object.desc)
rescue => detail
Puppet.log_exception(detail)
end
str << "\n\n"
# Now print the data about the item.
- str << ""
val = object.default
if name.to_s == "vardir"
val = "/var/lib/puppet"
diff --git a/lib/puppet/reference/indirection.rb b/lib/puppet/reference/indirection.rb
index e997c88e9..b1685eca1 100644
--- a/lib/puppet/reference/indirection.rb
+++ b/lib/puppet/reference/indirection.rb
@@ -10,16 +10,14 @@ reference = Puppet::Util::Reference.newreference :indirection, :doc => "Indirect
name = indirection.to_s.capitalize
text << "## " + indirection.to_s + "\n\n"
- text << ind.doc + "\n\n"
-
- text << "### Termini\n\n"
+ text << Puppet::Util::Docs.scrub(ind.doc) + "\n\n"
Puppet::Indirector::Terminus.terminus_classes(ind.name).sort { |a,b| a.to_s <=> b.to_s }.each do |terminus|
terminus_name = terminus.to_s
term_class = Puppet::Indirector::Terminus.terminus_class(ind.name, terminus)
if term_class
terminus_doc = Puppet::Util::Docs.scrub(term_class.doc)
- text << markdown_definitionlist(terminus_name, terminus_doc)
+ text << markdown_header("`#{terminus_name}` terminus", 3) << terminus_doc << "\n\n"
else
Puppet.warning "Could not build docs for indirector #{name.inspect}, terminus #{terminus_name.inspect}: could not locate terminus."
end
@@ -31,7 +29,7 @@ end
reference.header = <<HEADER
-# About Indirection
+## About Indirection
Puppet's indirector support pluggable backends (termini) for a variety of key-value stores (indirections).
Each indirection type corresponds to a particular Ruby class (the "Indirected Class" below) and values are instances of that class.
@@ -40,15 +38,15 @@ The termini can be local (e.g., on-disk files) or remote (e.g., using a REST int
An indirector has five methods, which are mapped into HTTP verbs for the REST interface:
- * `find(key)` - get a single value (mapped to GET or POST with a singular endpoint)
- * `search(key)` - get a list of matching values (mapped to GET with a plural endpoint)
- * `head(key)` - return true if the key exists (mapped to HEAD)
- * `destroy(key)` - remove the key van value (mapped to DELETE)
- * `save(instance)` - write the instance to the store, using the instance's name as the key (mapped to PUT)
+* `find(key)` - get a single value (mapped to GET or POST with a singular endpoint)
+* `search(key)` - get a list of matching values (mapped to GET with a plural endpoint)
+* `head(key)` - return true if the key exists (mapped to HEAD)
+* `destroy(key)` - remove the key van value (mapped to DELETE)
+* `save(instance)` - write the instance to the store, using the instance's name as the key (mapped to PUT)
-These methods are available via the `indirection` class method on the indirected classes. For example::
+These methods are available via the `indirection` class method on the indirected classes. For example:
- foo_cert = Puppet::SSL::Certificate.indirection.find('foo.example.com')
+ foo_cert = Puppet::SSL::Certificate.indirection.find('foo.example.com')
At startup, each indirection is configured with a terminus.
In most cases, this is the default terminus defined by the indirected class, but it can be overridden by the application or face, or overridden with the `route_file` configuration.
@@ -58,12 +56,12 @@ Indirections can also have a cache, represented by a second terminus.
This is a write-through cache: modifications are written both to the cache and to the primary terminus.
Values fetched from the terminus are written to the cache.
-## Interaction with REST
+### Interaction with REST
REST endpoints have the form `/{environment}/{indirection}/{key}`, where the indirection can be singular or plural, following normal English spelling rules.
On the server side, REST responses are generated from the locally-configured endpoints.
-## Indirections and Termini
+### Indirections and Termini
Below is the list of all indirections, their associated terminus classes, and how you select between them.
diff --git a/lib/puppet/relationship.rb b/lib/puppet/relationship.rb
index 2ffcd298f..ebac97e7e 100755..100644
--- a/lib/puppet/relationship.rb
+++ b/lib/puppet/relationship.rb
@@ -1,5 +1,3 @@
-#!/usr/bin/env ruby
-
# subscriptions are permanent associations determining how different
# objects react to an event
@@ -72,7 +70,7 @@ class Puppet::Relationship
"{ #{source} => #{target} }"
end
- def to_pson_data_hash
+ def to_data_hash
data = {
'source' => source.to_s,
'target' => target.to_s
@@ -85,8 +83,13 @@ class Puppet::Relationship
data
end
+ # This doesn't include document type as it is part of a catalog
+ def to_pson_data_hash
+ to_data_hash
+ end
+
def to_pson(*args)
- to_pson_data_hash.to_pson(*args)
+ to_data_hash.to_pson(*args)
end
def to_s
diff --git a/lib/puppet/reports.rb b/lib/puppet/reports.rb
index be6e8203f..e219971fb 100755..100644
--- a/lib/puppet/reports.rb
+++ b/lib/puppet/reports.rb
@@ -72,9 +72,9 @@ class Puppet::Reports
instance_loader(:report).loadall
loaded_instances(:report).sort { |a,b| a.to_s <=> b.to_s }.each do |name|
mod = self.report(name)
- docs += "#{name}\n#{"-" * name.to_s.length}\n"
+ docs << "#{name}\n#{"-" * name.to_s.length}\n"
- docs += Puppet::Util::Docs.scrub(mod.doc) + "\n\n"
+ docs << Puppet::Util::Docs.scrub(mod.doc) << "\n\n"
end
docs
diff --git a/lib/puppet/reports/rrdgraph.rb b/lib/puppet/reports/rrdgraph.rb
index f283f7c27..e55d653c4 100644
--- a/lib/puppet/reports/rrdgraph.rb
+++ b/lib/puppet/reports/rrdgraph.rb
@@ -114,7 +114,7 @@ Puppet::Reports.register_report(:rrdgraph) do
metric.graph
end
- mkhtml unless FileTest.exists?(File.join(hostdir, "index.html"))
+ mkhtml unless Puppet::FileSystem::File.exist?(File.join(hostdir, "index.html"))
end
# Unfortunately, RRD does not deal well with changing lists of values,
diff --git a/lib/puppet/reports/store.rb b/lib/puppet/reports/store.rb
index 7d318ffca..27d649355 100644
--- a/lib/puppet/reports/store.rb
+++ b/lib/puppet/reports/store.rb
@@ -17,7 +17,7 @@ Puppet::Reports.register_report(:store) do
dir = File.join(Puppet[:reportdir], host)
- if ! FileTest.exists?(dir)
+ if ! Puppet::FileSystem::File.exist?(dir)
FileUtils.mkdir_p(dir)
FileUtils.chmod_R(0750, dir)
end
@@ -54,11 +54,11 @@ Puppet::Reports.register_report(:store) do
dir = File.join(Puppet[:reportdir], host)
- if File.exists?(dir)
+ if Puppet::FileSystem::File.exist?(dir)
Dir.entries(dir).each do |file|
next if ['.','..'].include?(file)
file = File.join(dir, file)
- File.unlink(file) if File.file?(file)
+ Puppet::FileSystem::File.unlink(file) if File.file?(file)
end
Dir.rmdir(dir)
end
diff --git a/lib/puppet/reports/tagmail.rb b/lib/puppet/reports/tagmail.rb
index 528b6fa38..a07f98986 100644
--- a/lib/puppet/reports/tagmail.rb
+++ b/lib/puppet/reports/tagmail.rb
@@ -108,7 +108,7 @@ Puppet::Reports.register_report(:tagmail) do
# Process the report. This just calls the other associated messages.
def process
- unless FileTest.exists?(Puppet[:tagmap])
+ unless Puppet::FileSystem::File.exist?(Puppet[:tagmap])
Puppet.notice "Cannot send tagmail report; no tagmap file #{Puppet[:tagmap]}"
return
end
@@ -158,7 +158,7 @@ Puppet::Reports.register_report(:tagmail) do
p.puts "From: #{Puppet[:reportfrom]}"
p.puts "Subject: Puppet Report for #{self.host}"
p.puts "To: " + emails.join(", ")
-
+ p.puts
p.puts messages
end
end
diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb
index eae59f389..d4eac6f6d 100644
--- a/lib/puppet/resource.rb
+++ b/lib/puppet/resource.rb
@@ -56,7 +56,7 @@ class Puppet::Resource
"#{@type}[#{@title}]#{to_hash.inspect}"
end
- def to_pson_data_hash
+ def to_data_hash
data = ([:type, :title, :tags] + ATTRIBUTES).inject({}) do |hash, param|
next hash unless value = self.send(param)
hash[param.to_s] = value
@@ -80,6 +80,11 @@ class Puppet::Resource
data
end
+ # This doesn't include document type as it is part of a catalog
+ def to_pson_data_hash
+ to_data_hash
+ end
+
def self.value_to_pson_data(value)
if value.is_a? Array
value.map{|v| value_to_pson_data(v) }
@@ -102,8 +107,20 @@ class Puppet::Resource
end
end
+ YAML_ATTRIBUTES = [:@file, :@line, :@exported, :@type, :@title, :@tags, :@parameters]
+
+ # Explicitly list the instance variables that should be serialized when
+ # converting to YAML.
+ #
+ # @api private
+ # @return [Array<Symbol>] The intersection of our explicit variable list and
+ # all of the instance variables defined on this class.
+ def to_yaml_properties
+ YAML_ATTRIBUTES & super
+ end
+
def to_pson(*args)
- to_pson_data_hash.to_pson(*args)
+ to_data_hash.to_pson(*args)
end
# Proxy these methods to the parameters hash. It's likely they'll
@@ -328,10 +345,7 @@ class Puppet::Resource
result = scope.compiler.injector.lookup(scope, name)
end
if result.nil?
- Puppet::DataBinding.indirection.find(
- name,
- :environment => scope.environment.to_s,
- :variables => Puppet::DataBinding::Variables.new(scope))
+ lookup_with_databinding(name, scope)
else
result
end
@@ -339,6 +353,19 @@ class Puppet::Resource
private :lookup_external_default_for
+ def lookup_with_databinding(name, scope)
+ begin
+ Puppet::DataBinding.indirection.find(
+ name,
+ :environment => scope.environment.to_s,
+ :variables => scope)
+ rescue Puppet::DataBinding::LookupError => e
+ raise Puppet::Error.new("Error from DataBinding '#{Puppet[:data_binding_terminus]}' while looking up '#{name}': #{e.message}", e)
+ end
+ end
+
+ private :lookup_with_databinding
+
def set_default_parameters(scope)
return [] unless resource_type and resource_type.respond_to?(:arguments)
@@ -362,8 +389,39 @@ class Puppet::Resource
end.compact
end
- def to_resource
- self
+ def copy_as_resource
+ result = Puppet::Resource.new(type, title)
+
+ to_hash.each do |p, v|
+ if v.is_a?(Puppet::Resource)
+ v = Puppet::Resource.new(v.type, v.title)
+ elsif v.is_a?(Array)
+ # flatten resource references arrays
+ v = v.flatten if v.flatten.find { |av| av.is_a?(Puppet::Resource) }
+ v = v.collect do |av|
+ av = Puppet::Resource.new(av.type, av.title) if av.is_a?(Puppet::Resource)
+ av
+ end
+ end
+
+ # If the value is an array with only one value, then
+ # convert it to a single value. This is largely so that
+ # the database interaction doesn't have to worry about
+ # whether it returns an array or a string.
+ result[p] = if v.is_a?(Array) and v.length == 1
+ v[0]
+ else
+ v
+ end
+ end
+
+ result.file = self.file
+ result.line = self.line
+ result.exported = self.exported
+ result.virtual = self.virtual
+ result.tag(*self.tags)
+
+ result
end
def valid_parameter?(name)
diff --git a/lib/puppet/resource/catalog.rb b/lib/puppet/resource/catalog.rb
index 3120d3ff7..61b4f0b81 100644
--- a/lib/puppet/resource/catalog.rb
+++ b/lib/puppet/resource/catalog.rb
@@ -365,19 +365,23 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph
result.add_edge(edge)
end
+ def to_data_hash
+ {
+ 'tags' => tags,
+ 'name' => name,
+ 'version' => version,
+ 'environment' => environment.to_s,
+ 'resources' => @resources.collect { |v| @resource_table[v].to_pson_data_hash },
+ 'edges' => edges. collect { |e| e.to_pson_data_hash },
+ 'classes' => classes
+ }
+ end
+
PSON.register_document_type('Catalog',self)
def to_pson_data_hash
{
'document_type' => 'Catalog',
- 'data' => {
- 'tags' => tags,
- 'name' => name,
- 'version' => version,
- 'environment' => environment.to_s,
- 'resources' => @resources.collect { |v| @resource_table[v].to_pson_data_hash },
- 'edges' => edges. collect { |e| e.to_pson_data_hash },
- 'classes' => classes
- },
+ 'data' => to_data_hash,
'metadata' => {
'api_version' => 1
}
@@ -465,7 +469,7 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph
# Verify that the given resource isn't declared elsewhere.
def fail_on_duplicate_type_and_title(resource)
- # Short-curcuit the common case,
+ # Short-circuit the common case,
return unless existing_resource = @resource_table[title_key_for_ref(resource.ref)]
# If we've gotten this far, it's a real conflict
@@ -492,22 +496,11 @@ class Puppet::Resource::Catalog < Puppet::Graph::SimpleGraph
next if virtual_not_exported?(resource)
next if block_given? and yield resource
- #This is hackity hack for 1094
- #Aliases aren't working in the ral catalog because the current instance of the resource
- #has a reference to the catalog being converted. . . So, give it a reference to the new one
- #problem solved. . .
- if resource.class == Puppet::Resource
- resource = resource.dup
- resource.catalog = result
- elsif resource.is_a?(Puppet::Parser::Resource)
- resource = resource.to_resource
- resource.catalog = result
- end
+ newres = resource.copy_as_resource
+ newres.catalog = result
- if resource.is_a?(Puppet::Resource) and convert.to_s == "to_resource"
- newres = resource
- else
- newres = resource.send(convert)
+ if convert != :to_resource
+ newres = newres.to_ral
end
# We can't guarantee that resources don't munge their names
diff --git a/lib/puppet/resource/status.rb b/lib/puppet/resource/status.rb
index 6bfe73b2b..eae5aeed9 100644
--- a/lib/puppet/resource/status.rb
+++ b/lib/puppet/resource/status.rb
@@ -1,10 +1,12 @@
require 'time'
+require 'puppet/network/format_support'
module Puppet
class Resource
class Status
include Puppet::Util::Tagging
include Puppet::Util::Logging
+ include Puppet::Network::FormatSupport
attr_accessor :resource, :node, :file, :line, :current_values, :status, :evaluation_time
@@ -65,7 +67,7 @@ module Puppet
# will always be accompanied by an event with some explanatory power. This
# is useful for reporting/diagnostics/etc. So synthesize an event here
# with the exception detail as the message.
- add_event(@real_resource.event(:status => "failure", :message => detail.to_s))
+ add_event(@real_resource.event(:name => :resource_error, :status => "failure", :message => detail.to_s))
end
def initialize(resource)
@@ -100,7 +102,7 @@ module Puppet
@evaluation_time = data['evaluation_time']
@change_count = data['change_count']
@out_of_sync_count = data['out_of_sync_count']
- @tags = data['tags']
+ @tags = Puppet::Util::TagSet.new(data['tags'])
@time = data['time']
@time = Time.parse(@time) if @time.is_a? String
@out_of_sync = data['out_of_sync']
@@ -113,7 +115,7 @@ module Puppet
end
end
- def to_pson
+ def to_data_hash
{
'title' => @title,
'file' => @file,
@@ -131,7 +133,11 @@ module Puppet
'change_count' => @change_count,
'out_of_sync_count' => @out_of_sync_count,
'events' => @events,
- }.to_pson
+ }
+ end
+
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
def to_yaml_properties
diff --git a/lib/puppet/run.rb b/lib/puppet/run.rb
index 762244119..1d89304be 100644
--- a/lib/puppet/run.rb
+++ b/lib/puppet/run.rb
@@ -94,11 +94,15 @@ class Puppet::Run
new(options)
end
- def to_pson
+ def to_data_hash
{
:options => @options,
:background => @background,
:status => @status
- }.to_pson
+ }
+ end
+
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
end
diff --git a/lib/puppet/settings.rb b/lib/puppet/settings.rb
index b47039b66..ca017ae1e 100644
--- a/lib/puppet/settings.rb
+++ b/lib/puppet/settings.rb
@@ -1,5 +1,4 @@
require 'puppet'
-require 'sync'
require 'getoptlong'
require 'puppet/util/watched_file'
require 'puppet/util/command_line/puppet_option_parser'
@@ -18,6 +17,8 @@ class Puppet::Settings
require 'puppet/settings/boolean_setting'
require 'puppet/settings/terminus_setting'
require 'puppet/settings/duration_setting'
+ require 'puppet/settings/priority_setting'
+ require 'puppet/settings/autosign_setting'
require 'puppet/settings/config_file'
require 'puppet/settings/value_translator'
@@ -74,9 +75,6 @@ class Puppet::Settings
@created = []
@searchpath = nil
- # Mutex-like thing to protect @values
- @sync = Sync.new
-
# Keep track of set values.
@values = Hash.new { |hash, key| hash[key] = {} }
@@ -95,6 +93,12 @@ class Puppet::Settings
@config_file_parser = Puppet::Settings::ConfigFile.new(@translate)
end
+ # @param name [Symbol] The name of the setting to fetch
+ # @return [Puppet::Settings::BaseSetting] The setting object
+ def setting(name)
+ @config[name]
+ end
+
# Retrieve a config value
def [](param)
value(param)
@@ -135,9 +139,7 @@ class Puppet::Settings
# Remove all set values, potentially skipping cli values.
def clear
- @sync.synchronize do
- unsafe_clear
- end
+ unsafe_clear
end
# Remove all set values, potentially skipping cli values.
@@ -170,9 +172,7 @@ class Puppet::Settings
# this method must be called to clear out the caches so that updated
# objects will be returned.
def flush_cache
- @sync.synchronize do
- unsafe_flush_cache
- end
+ unsafe_flush_cache
end
def unsafe_flush_cache
@@ -507,9 +507,7 @@ class Puppet::Settings
# Parse the configuration file. Just provides thread safety.
def parse_config_files
- @sync.synchronize do
- unsafe_parse(which_configuration_file)
- end
+ unsafe_parse(which_configuration_file)
call_hooks_deferred_to_application_initialization :ignore_interpolation_dependency_errors => true
end
@@ -552,7 +550,7 @@ class Puppet::Settings
def unsafe_parse(file)
# build up a single data structure that contains the values from all of the parsed files.
data = {}
- if FileTest.exist?(file)
+ if Puppet::FileSystem::File.exist?(file)
begin
file_data = parse_file(file)
@@ -602,7 +600,11 @@ class Puppet::Settings
# This results in extra work, but so few of the settings
# will have associated hooks that it ends up being less work this
# way overall.
- setting.handle(self.value(setting.name, env))
+ if setting.call_hook_on_initialize?
+ @hooks_to_call_on_application_initialization << setting
+ else
+ setting.handle(self.value(setting.name, env))
+ end
break
end
end
@@ -636,6 +638,8 @@ class Puppet::Settings
:terminus => TerminusSetting,
:duration => DurationSetting,
:enum => EnumSetting,
+ :priority => PrioritySetting,
+ :autosign => AutosignSetting,
}
# Create a new setting. The value is passed in because it's used to determine
@@ -692,7 +696,7 @@ class Puppet::Settings
return @files if @files
@files = []
[main_config_file, user_config_file].each do |path|
- if FileTest.exist?(path)
+ if Puppet::FileSystem::File.exist?(path)
@files << Puppet::Util::WatchedFile.new(path)
end
end
@@ -713,11 +717,9 @@ class Puppet::Settings
def reuse
return unless defined?(@used)
- @sync.synchronize do # yay, thread-safe
- new = @used
- @used = []
- self.use(*new)
- end
+ new = @used
+ @used = []
+ self.use(*new)
end
# The order in which to search for values.
@@ -788,12 +790,8 @@ class Puppet::Settings
setting.handle(value) if setting.has_hook? and not options[:dont_trigger_handles]
- @sync.synchronize do # yay, thread-safe
-
- @values[type][param] = value
- unsafe_flush_cache
-
- end
+ @values[type][param] = value
+ unsafe_flush_cache
value
end
@@ -839,7 +837,7 @@ class Puppet::Settings
def define_settings(section, defs)
section = section.to_sym
call = []
- defs.each { |name, hash|
+ defs.each do |name, hash|
raise ArgumentError, "setting definition for '#{name}' is not a hash!" unless hash.is_a? Hash
name = name.to_sym
@@ -858,9 +856,12 @@ class Puppet::Settings
# Collect the settings that need to have their hooks called immediately.
# We have to collect them so that we can be sure we're fully initialized before
# the hook is called.
- call << tryconfig if tryconfig.call_hook_on_define?
- @hooks_to_call_on_application_initialization << tryconfig if tryconfig.call_hook_on_initialize?
- }
+ if tryconfig.call_hook_on_define?
+ call << tryconfig
+ elsif tryconfig.call_hook_on_initialize?
+ @hooks_to_call_on_application_initialization << tryconfig
+ end
+ end
call.each { |setting| setting.handle(self.value(setting.name)) }
end
@@ -927,29 +928,27 @@ Generated on #{Time.now}.
# you can 'use' a section as many times as you want.
def use(*sections)
sections = sections.collect { |s| s.to_sym }
- @sync.synchronize do # yay, thread-safe
- sections = sections.reject { |s| @used.include?(s) }
+ sections = sections.reject { |s| @used.include?(s) }
- return if sections.empty?
+ return if sections.empty?
- begin
- catalog = to_catalog(*sections).to_ral
- rescue => detail
- Puppet.log_and_raise(detail, "Could not create resources for managing Puppet's files and directories in sections #{sections.inspect}: #{detail}")
- end
+ begin
+ catalog = to_catalog(*sections).to_ral
+ rescue => detail
+ Puppet.log_and_raise(detail, "Could not create resources for managing Puppet's files and directories in sections #{sections.inspect}: #{detail}")
+ end
- catalog.host_config = false
- catalog.apply do |transaction|
- if transaction.any_failed?
- report = transaction.report
- failures = report.logs.find_all { |log| log.level == :err }
- raise "Got #{failures.length} failure(s) while initializing: #{failures.collect { |l| l.to_s }.join("; ")}"
- end
+ catalog.host_config = false
+ catalog.apply do |transaction|
+ if transaction.any_failed?
+ report = transaction.report
+ failures = report.logs.find_all { |log| log.level == :err }
+ raise "Got #{failures.length} failure(s) while initializing: #{failures.collect { |l| l.to_s }.join("; ")}"
end
-
- sections.each { |s| @used << s }
- @used.uniq!
end
+
+ sections.each { |s| @used << s }
+ @used.uniq!
end
def valid?(param)
@@ -974,9 +973,7 @@ Generated on #{Time.now}.
each_source(environment) do |source|
# Look for the value. We have to test the hash for whether
# it exists, because the value might be false.
- @sync.synchronize do
- return @values[source][param] if @values[source].include?(param)
- end
+ return @values[source][param] if @values[source].include?(param)
end
return nil
end
@@ -1033,70 +1030,6 @@ Generated on #{Time.now}.
val
end
- # Open a file with the appropriate user, group, and mode
- def write(default, *args, &bloc)
- obj = get_config_file_default(default)
- writesub(default, value(obj.name), *args, &bloc)
- end
-
- # Open a non-default file under a default dir with the appropriate user,
- # group, and mode
- def writesub(default, file, *args, &bloc)
- obj = get_config_file_default(default)
- chown = nil
- if Puppet.features.root?
- chown = [obj.owner, obj.group]
- else
- chown = [nil, nil]
- end
-
- Puppet::Util::SUIDManager.asuser(*chown) do
- mode = obj.mode ? obj.mode.to_i : 0640
- args << "w" if args.empty?
-
- args << mode
-
- # Update the umask to make non-executable files
- Puppet::Util.withumask(File.umask ^ 0111) do
- File.open(file, *args) do |file|
- yield file
- end
- end
- end
- end
-
- def readwritelock(default, *args, &bloc)
- file = value(get_config_file_default(default).name)
- tmpfile = file + ".tmp"
- sync = Sync.new
- raise Puppet::DevError, "Cannot create #{file}; directory #{File.dirname(file)} does not exist" unless FileTest.directory?(File.dirname(tmpfile))
-
- sync.synchronize(Sync::EX) do
- File.open(file, ::File::CREAT|::File::RDWR, 0600) do |rf|
- rf.lock_exclusive do
- if File.exist?(tmpfile)
- raise Puppet::Error, ".tmp file already exists for #{file}; Aborting locked write. Check the .tmp file and delete if appropriate"
- end
-
- # If there's a failure, remove our tmpfile
- begin
- writesub(default, tmpfile, *args, &bloc)
- rescue
- File.unlink(tmpfile) if FileTest.exist?(tmpfile)
- raise
- end
-
- begin
- File.rename(tmpfile, file)
- rescue => detail
- Puppet.err "Could not rename #{file} to #{tmpfile}: #{detail}"
- File.unlink(tmpfile) if FileTest.exist?(tmpfile)
- end
- end
- end
- end
- end
-
private
def get_config_file_default(default)
@@ -1166,9 +1099,7 @@ Generated on #{Time.now}.
def set_metadata(meta)
meta.each do |var, values|
values.each do |param, value|
- @sync.synchronize do # yay, thread-safe
- @config[var].send(param.to_s + "=", value)
- end
+ @config[var].send(param.to_s + "=", value)
end
end
end
@@ -1177,11 +1108,9 @@ Generated on #{Time.now}.
#
# @return nil
def clear_everything_for_tests()
- @sync.synchronize do
- unsafe_clear(true, true)
- @global_defaults_initialized = false
- @app_defaults_initialized = false
- end
+ unsafe_clear(true, true)
+ @global_defaults_initialized = false
+ @app_defaults_initialized = false
end
private :clear_everything_for_tests
diff --git a/lib/puppet/settings/autosign_setting.rb b/lib/puppet/settings/autosign_setting.rb
new file mode 100644
index 000000000..f13824ecf
--- /dev/null
+++ b/lib/puppet/settings/autosign_setting.rb
@@ -0,0 +1,20 @@
+require 'puppet/settings/base_setting'
+
+class Puppet::Settings::AutosignSetting < Puppet::Settings::BaseSetting
+
+ def type
+ :autosign
+ end
+
+ def munge(value)
+ if ['true', true].include? value
+ true
+ elsif ['false', false, nil].include? value
+ false
+ elsif Puppet::Util.absolute_path?(value)
+ value
+ else
+ raise Puppet::Settings::ValidationError, "Invalid autosign value #{value}: must be 'true'/'false' or an absolute path"
+ end
+ end
+end
diff --git a/lib/puppet/settings/base_setting.rb b/lib/puppet/settings/base_setting.rb
index 92c904e90..ac38b35f3 100644
--- a/lib/puppet/settings/base_setting.rb
+++ b/lib/puppet/settings/base_setting.rb
@@ -2,17 +2,13 @@ require 'puppet/settings/errors'
# The base setting type
class Puppet::Settings::BaseSetting
- attr_accessor :name, :section, :default, :call_on_define, :call_hook
- attr_reader :desc, :short
+ attr_accessor :name, :desc, :section, :default, :call_on_define, :call_hook
+ attr_reader :short
def self.available_call_hook_values
[:on_define_and_write, :on_initialize_and_write, :on_write_only]
end
- def desc=(value)
- @desc = value.gsub(/^\s*/, '')
- end
-
def call_on_define
Puppet.deprecation_warning "call_on_define has been deprecated. Please use call_hook_on_define?"
call_hook_on_define?
@@ -130,10 +126,12 @@ class Puppet::Settings::BaseSetting
# Convert the object to a config statement.
def to_config
- str = @desc.gsub(/^/, "# ") + "\n"
+ require 'puppet/util/docs'
+ # Scrub any funky indentation; comment out description.
+ str = Puppet::Util::Docs.scrub(@desc).gsub(/^/, "# ") + "\n"
# Add in a statement about the default.
- str += "# The default value is '#{default(true)}'.\n" if default(true)
+ str << "# The default value is '#{default(true)}'.\n" if default(true)
# If the value has not been overridden, then print it out commented
# and unconverted, so it's clear that that's the default and how it
@@ -146,8 +144,9 @@ class Puppet::Settings::BaseSetting
line = "# #{@name} = #{@default}"
end
- str += line + "\n"
+ str << (line + "\n")
+ # Indent
str.gsub(/^/, " ")
end
diff --git a/lib/puppet/settings/directory_setting.rb b/lib/puppet/settings/directory_setting.rb
index 2a9472f92..9f67f96ab 100644
--- a/lib/puppet/settings/directory_setting.rb
+++ b/lib/puppet/settings/directory_setting.rb
@@ -2,4 +2,12 @@ class Puppet::Settings::DirectorySetting < Puppet::Settings::FileSetting
def type
:directory
end
+
+ # @api private
+ def open_file(filename, option = 'r', &block)
+ file = Puppet::FileSystem::File.new(filename)
+ controlled_access do |mode|
+ file.open(mode, option, &block)
+ end
+ end
end
diff --git a/lib/puppet/settings/file_setting.rb b/lib/puppet/settings/file_setting.rb
index 265bc0613..243805edb 100644
--- a/lib/puppet/settings/file_setting.rb
+++ b/lib/puppet/settings/file_setting.rb
@@ -128,7 +128,7 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting
# Make sure the paths are fully qualified.
path = File.expand_path(path)
- return nil unless type == :directory or create_files? or File.exist?(path)
+ return nil unless type == :directory or create_files? or Puppet::FileSystem::File.exist?(path)
return nil if path =~ /^\/dev/ or path =~ /^[A-Z]:\/dev/i
resource = Puppet::Resource.new(:file, path)
@@ -178,8 +178,42 @@ class Puppet::Settings::FileSetting < Puppet::Settings::StringSetting
}
end
+ # @api private
+ def exclusive_open(option = 'r', &block)
+ controlled_access do |mode|
+ file.exclusive_open(mode, option, &block)
+ end
+ end
+
+ # @api private
+ def open(option = 'r', &block)
+ controlled_access do |mode|
+ file.open(mode, option, &block)
+ end
+ end
+
private
+ def file
+ Puppet::FileSystem::File.new(value)
+ end
+
def unknown_value(parameter, value)
raise SettingError, "The #{parameter} parameter for the setting '#{name}' must be either 'root' or 'service', not '#{value}'"
end
+
+ def controlled_access(&block)
+ chown = nil
+ if Puppet.features.root?
+ chown = [owner, group]
+ else
+ chown = [nil, nil]
+ end
+
+ Puppet::Util::SUIDManager.asuser(*chown) do
+ # Update the umask to make non-executable files
+ Puppet::Util.withumask(File.umask ^ 0111) do
+ yield mode ? mode.to_i : 0640
+ end
+ end
+ end
end
diff --git a/lib/puppet/settings/priority_setting.rb b/lib/puppet/settings/priority_setting.rb
new file mode 100644
index 000000000..707b8ab82
--- /dev/null
+++ b/lib/puppet/settings/priority_setting.rb
@@ -0,0 +1,42 @@
+require 'puppet/settings/base_setting'
+
+# A setting that represents a scheduling priority, and evaluates to an
+# OS-specific priority level.
+class Puppet::Settings::PrioritySetting < Puppet::Settings::BaseSetting
+ PRIORITY_MAP =
+ if Puppet::Util::Platform.windows?
+ require 'win32/process'
+ {
+ :high => Process::HIGH_PRIORITY_CLASS,
+ :normal => Process::NORMAL_PRIORITY_CLASS,
+ :low => Process::BELOW_NORMAL_PRIORITY_CLASS,
+ :idle => Process::IDLE_PRIORITY_CLASS
+ }
+ else
+ {
+ :high => -10,
+ :normal => 0,
+ :low => 10,
+ :idle => 19
+ }
+ end
+
+ def type
+ :priority
+ end
+
+ def munge(value)
+ return unless value
+
+ case
+ when value.is_a?(Integer)
+ value
+ when (value.is_a?(String) and value =~ /\d+/)
+ value.to_i
+ when (value.is_a?(String) and PRIORITY_MAP[value.to_sym])
+ PRIORITY_MAP[value.to_sym]
+ else
+ raise Puppet::Settings::ValidationError, "Invalid priority format '#{value.inspect}' for parameter: #{@name}"
+ end
+ end
+end
diff --git a/lib/puppet/ssl.rb b/lib/puppet/ssl.rb
index 8f71ba8a4..596feb933 100644
--- a/lib/puppet/ssl.rb
+++ b/lib/puppet/ssl.rb
@@ -5,4 +5,8 @@ require 'openssl'
module Puppet::SSL # :nodoc:
CA_NAME = "ca"
require 'puppet/ssl/host'
+ require 'puppet/ssl/oids'
+ require 'puppet/ssl/validator'
+ require 'puppet/ssl/validator/no_validator'
+ require 'puppet/ssl/validator/default_validator'
end
diff --git a/lib/puppet/ssl/certificate.rb b/lib/puppet/ssl/certificate.rb
index a49267ac7..a8ed118fa 100644
--- a/lib/puppet/ssl/certificate.rb
+++ b/lib/puppet/ssl/certificate.rb
@@ -44,4 +44,22 @@ DOC
def unmunged_name
self.class.name_from_subject(content.subject)
end
+
+ # Any extensions registered with custom OIDs as defined in module
+ # Puppet::SSL::Oids may be looked up here.
+ #
+ # A cert with a 'pp_uuid' extension having the value 'abcd' would return:
+ #
+ # [{ 'oid' => 'pp_uuid', 'value' => 'abcd'}]
+ #
+ # @return [Array<Hash{String => String}>] An array of two element hashes,
+ # with key/value pairs for the extension's oid, and its value.
+ def custom_extensions
+ custom_exts = content.extensions.select do |ext|
+ Puppet::SSL::Oids.subtree_of?('ppRegCertExt', ext.oid) or
+ Puppet::SSL::Oids.subtree_of?('ppPrivCertExt', ext.oid)
+ end
+
+ custom_exts.map { |ext| {'oid' => ext.oid, 'value' => ext.value} }
+ end
end
diff --git a/lib/puppet/ssl/certificate_authority.rb b/lib/puppet/ssl/certificate_authority.rb
index 83ff6a114..3ff4d5eb5 100644
--- a/lib/puppet/ssl/certificate_authority.rb
+++ b/lib/puppet/ssl/certificate_authority.rb
@@ -1,4 +1,3 @@
-require 'monitor'
require 'puppet/ssl/host'
require 'puppet/ssl/certificate_request'
require 'puppet/ssl/certificate_signer'
@@ -26,10 +25,9 @@ class Puppet::SSL::CertificateAuthority
require 'puppet/ssl/inventory'
require 'puppet/ssl/certificate_revocation_list'
require 'puppet/ssl/certificate_authority/interface'
+ require 'puppet/ssl/certificate_authority/autosign_command'
require 'puppet/network/authstore'
- extend MonitorMixin
-
class CertificateVerificationError < RuntimeError
attr_accessor :error_code
@@ -39,9 +37,7 @@ class Puppet::SSL::CertificateAuthority
end
def self.singleton_instance
- synchronize do
- @singleton_instance ||= new
- end
+ @singleton_instance ||= new
end
class CertificateSigningError < RuntimeError
@@ -53,67 +49,56 @@ class Puppet::SSL::CertificateAuthority
end
def self.ca?
- return false unless Puppet[:ca]
- return false unless Puppet.run_mode.master?
- true
+ # running as ca? - ensure boolean answer
+ !!(Puppet[:ca] && Puppet.run_mode.master?)
end
- # If this process can function as a CA, then return a singleton
- # instance.
+ # If this process can function as a CA, then return a singleton instance.
def self.instance
- return nil unless ca?
-
- singleton_instance
+ ca? ? singleton_instance : nil
end
attr_reader :name, :host
- # Create and run an applicator. I wanted to build an interface where you could do
- # something like 'ca.apply(:generate).to(:all) but I don't think it's really possible.
- def apply(method, options)
- raise ArgumentError, "You must specify the hosts to apply to; valid values are an array or the symbol :all" unless options[:to]
- applier = Interface.new(method, options)
- applier.apply(self)
- end
-
- # If autosign is configured, then autosign all CSRs that match our configuration.
- def autosign
- return unless auto = autosign?
-
- store = nil
- store = autosign_store(auto) if auto != true
-
- Puppet::SSL::CertificateRequest.indirection.search("*").each do |csr|
- if auto == true or store.allowed?(csr.name, "127.1.1.1")
- Puppet.info "Autosigning #{csr.name}"
- sign(csr.name)
- end
+ # If autosign is configured, autosign the csr we are passed.
+ # @param csr [Puppet::SSL::CertificateRequest] The csr to sign.
+ # @return [Void]
+ # @api private
+ def autosign(csr)
+ if autosign?(csr)
+ Puppet.info "Autosigning #{csr.name}"
+ sign(csr.name)
end
end
- # Do we autosign? This returns true, false, or a filename.
- def autosign?
+ # Determine if a CSR can be autosigned by the autosign store or autosign command
+ #
+ # @param csr [Puppet::SSL::CertificateRequest] The CSR to check
+ # @return [true, false]
+ # @api private
+ def autosign?(csr)
auto = Puppet[:autosign]
- return false if ['false', false].include?(auto)
- return true if ['true', true].include?(auto)
- raise ArgumentError, "The autosign configuration '#{auto}' must be a fully qualified file" unless Puppet::Util.absolute_path?(auto)
- FileTest.exist?(auto) && auto
- end
-
- # Create an AuthStore for autosigning.
- def autosign_store(file)
- auth = Puppet::Network::AuthStore.new
- File.readlines(file).each do |line|
- next if line =~ /^\s*#/
- next if line =~ /^\s*$/
- auth.allow(line.chomp)
- end
+ decider = case auto
+ when 'false', false, nil
+ AutosignNever.new
+ when 'true', true
+ AutosignAlways.new
+ else
+ file = Puppet::FileSystem::File.new(auto)
+ if file.executable?
+ Puppet::SSL::CertificateAuthority::AutosignCommand.new(auto)
+ elsif file.exist?
+ AutosignConfig.new(file)
+ else
+ AutosignNever.new
+ end
+ end
- auth
+ decider.allowed?(csr)
end
- # Retrieve (or create, if necessary) the certificate revocation list.
+ # Retrieves (or creates, if necessary) the certificate revocation list.
def crl
unless defined?(@crl)
unless @crl = Puppet::SSL::CertificateRevocationList.indirection.find(Puppet::SSL::CA_NAME)
@@ -125,12 +110,12 @@ class Puppet::SSL::CertificateAuthority
@crl
end
- # Delegate this to our Host class.
+ # Delegates this to our Host class.
def destroy(name)
Puppet::SSL::Host.destroy(name)
end
- # Generate a new certificate.
+ # Generates a new certificate.
# @return Puppet::SSL::Certificate
def generate(name, options = {})
raise ArgumentError, "A Certificate already exists for #{name}" if Puppet::SSL::Certificate.indirection.find(name)
@@ -187,7 +172,7 @@ class Puppet::SSL::CertificateAuthority
20.times { pass += (rand(74) + 48).chr }
begin
- Puppet.settings.write(:capass) { |f| f.print pass }
+ Puppet.settings.setting(:capass).open('w') { |f| f.print pass }
rescue Errno::EACCES => detail
raise Puppet::Error, "Could not write CA password: #{detail}"
end
@@ -222,26 +207,27 @@ class Puppet::SSL::CertificateAuthority
# Read the next serial from the serial file, and increment the
# file so this one is considered used.
def next_serial
- serial = nil
-
- # This is slightly odd. If the file doesn't exist, our readwritelock creates
- # it, but with a mode we can't actually read in some cases. So, use
- # a default before the lock.
- serial = 0x1 unless FileTest.exist?(Puppet[:serial])
+ serial = 1
+ Puppet.settings.setting(:serial).exclusive_open('a+') do |f|
+ f.rewind
+ serial = f.read.chomp.hex
+ if serial == 0
+ serial = 1
+ end
- Puppet.settings.readwritelock(:serial) { |f|
- serial ||= File.read(Puppet.settings[:serial]).chomp.hex if FileTest.exist?(Puppet[:serial])
+ f.truncate(0)
+ f.rewind
# We store the next valid serial, not the one we just used.
f << "%04X" % (serial + 1)
- }
+ end
serial
end
# Does the password file exist?
def password?
- FileTest.exist? Puppet[:capass]
+ Puppet::FileSystem::File.exist? Puppet[:capass]
end
# Print a given host's certificate as text.
@@ -323,8 +309,11 @@ class Puppet::SSL::CertificateAuthority
def check_internal_signing_policies(hostname, csr, allow_dns_alt_names)
# Reject unknown request extensions.
- unknown_req = csr.request_extensions.
- reject {|x| RequestExtensionWhitelist.include? x["oid"] }
+ unknown_req = csr.request_extensions.reject do |x|
+ RequestExtensionWhitelist.include? x["oid"] or
+ Puppet::SSL::Oids.subtree_of?('ppRegCertExt', x["oid"], true) or
+ Puppet::SSL::Oids.subtree_of?('ppPrivCertExt', x["oid"], true)
+ end
if unknown_req and not unknown_req.empty?
names = unknown_req.map {|x| x["oid"] }.sort.uniq.join(", ")
@@ -393,7 +382,7 @@ class Puppet::SSL::CertificateAuthority
#
# @return [OpenSSL::X509::Store]
def x509_store(options = {})
- if (options[:cache])
+ if (options[:cache])
return @x509store unless @x509store.nil?
@x509store = create_x509_store
else
@@ -407,11 +396,13 @@ class Puppet::SSL::CertificateAuthority
#
# @return [OpenSSL::X509::Store]
def create_x509_store
- store = OpenSSL::X509::Store.new
- store.add_file Puppet[:cacert]
- store.add_crl crl.content if self.crl
+ store = OpenSSL::X509::Store.new()
+ store.add_file(Puppet[:cacert])
+ store.add_crl(crl.content) if self.crl
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
- store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL|OpenSSL::X509::V_FLAG_CRL_CHECK if Puppet.settings[:certificate_revocation]
+ if Puppet.settings[:certificate_revocation]
+ store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL | OpenSSL::X509::V_FLAG_CRL_CHECK
+ end
store
end
private :create_x509_store
@@ -472,4 +463,42 @@ class Puppet::SSL::CertificateAuthority
def waiting?
Puppet::SSL::CertificateRequest.indirection.search("*").collect { |r| r.name }
end
+
+ # @api private
+ class AutosignAlways
+ def allowed?(csr)
+ true
+ end
+ end
+
+ # @api private
+ class AutosignNever
+ def allowed?(csr)
+ false
+ end
+ end
+
+ # @api private
+ class AutosignConfig
+ def initialize(config_file)
+ @config = config_file
+ end
+
+ def allowed?(csr)
+ autosign_store.allowed?(csr.name, '127.1.1.1')
+ end
+
+ private
+
+ def autosign_store
+ auth = Puppet::Network::AuthStore.new
+ @config.each_line do |line|
+ next if line =~ /^\s*#/
+ next if line =~ /^\s*$/
+ auth.allow(line.chomp)
+ end
+
+ auth
+ end
+ end
end
diff --git a/lib/puppet/ssl/certificate_authority/autosign_command.rb b/lib/puppet/ssl/certificate_authority/autosign_command.rb
new file mode 100644
index 000000000..52c066555
--- /dev/null
+++ b/lib/puppet/ssl/certificate_authority/autosign_command.rb
@@ -0,0 +1,44 @@
+require 'puppet/ssl/certificate_authority'
+
+# This class wraps a given command and invokes it with a CSR name and body to
+# determine if the given CSR should be autosigned
+#
+# @api private
+class Puppet::SSL::CertificateAuthority::AutosignCommand
+
+ class CheckFailure < Puppet::Error; end
+
+ def initialize(path)
+ @path = path
+ end
+
+ # Run the autosign command with the given CSR name as an argument and the
+ # CSR body on stdin.
+ #
+ # @param name [String] The CSR name to check for autosigning
+ # @return [true, false] If the CSR should be autosigned
+ def allowed?(csr)
+ name = csr.name
+ cmd = [@path, name]
+
+ output = Puppet::FileSystem::Tempfile.open('puppet-csr') do |csr_file|
+ csr_file.write(csr.to_s)
+ csr_file.flush
+
+ execute_options = {:stdinfile => csr_file.path, :combine => true, :failonfail => false}
+ Puppet::Util::Execution.execute(cmd, execute_options)
+ end
+
+ output.chomp!
+
+ Puppet.debug "Autosign command '#{@path}' exit status: #{output.exitstatus}"
+ Puppet.debug "Autosign command '#{@path}' output: #{output}"
+
+ case output.exitstatus
+ when 0
+ true
+ else
+ false
+ end
+ end
+end
diff --git a/lib/puppet/ssl/certificate_authority/interface.rb b/lib/puppet/ssl/certificate_authority/interface.rb
index 4e7a14ac8..b68368b8d 100644
--- a/lib/puppet/ssl/certificate_authority/interface.rb
+++ b/lib/puppet/ssl/certificate_authority/interface.rb
@@ -4,7 +4,9 @@ module Puppet
# This class is basically a hidden class that knows how to act on the
# CA. Its job is to provide a CLI-like interface to the CA class.
class Interface
- INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, :verify, :fingerprint]
+ INTERFACE_METHODS = [:destroy, :list, :revoke, :generate, :sign, :print, :verify, :fingerprint, :reinventory]
+
+ SUBJECTLESS_METHODS = [:list, :reinventory]
class InterfaceError < ArgumentError; end
@@ -12,14 +14,17 @@ module Puppet
# Actually perform the work.
def apply(ca)
- unless subjects or method == :list
+ unless subjects || SUBJECTLESS_METHODS.include?(method)
raise ArgumentError, "You must provide hosts or --all when using #{method}"
end
- return send(method, ca) if respond_to?(method)
-
- (subjects == :all ? ca.list : subjects).each do |host|
- ca.send(method, host)
+ # if the interface implements the method, use it instead of the ca's method
+ if respond_to?(method)
+ send(method, ca)
+ else
+ (subjects == :all ? ca.list : subjects).each do |host|
+ ca.send(method, host)
+ end
end
end
@@ -66,14 +71,11 @@ module Puppet
end
if verify_error
- cert = Puppet::SSL::Certificate.indirection.find(host)
- certs[:invalid][host] = [cert, verify_error]
+ certs[:invalid][host] = [ Puppet::SSL::Certificate.indirection.find(host), verify_error ]
elsif signed.include?(host)
- cert = Puppet::SSL::Certificate.indirection.find(host)
- certs[:signed][host] = cert
+ certs[:signed][host] = Puppet::SSL::Certificate.indirection.find(host)
else
- req = Puppet::SSL::CertificateRequest.indirection.find(host)
- certs[:request][host] = req
+ certs[:request][host] = Puppet::SSL::CertificateRequest.indirection.find(host)
end
end
@@ -147,7 +149,7 @@ module Puppet
end
end
- # Sign a given certificate.
+ # Signs given certificates or waiting of subjects == :all
def sign(ca)
list = subjects == :all ? ca.waiting? : subjects
raise InterfaceError, "No waiting certificate requests to sign" if list.empty?
@@ -156,15 +158,17 @@ module Puppet
end
end
+ def reinventory(ca)
+ ca.inventory.rebuild
+ end
+
# Set the list of hosts we're operating on. Also supports keywords.
def subjects=(value)
- unless value == :all or value == :signed or value.is_a?(Array)
+ unless value == :all || value == :signed || value.is_a?(Array)
raise ArgumentError, "Subjects must be an array or :all; not #{value}"
end
- value = nil if value.is_a?(Array) and value.empty?
-
- @subjects = value
+ @subjects = (value == []) ? nil : value
end
end
end
diff --git a/lib/puppet/ssl/certificate_factory.rb b/lib/puppet/ssl/certificate_factory.rb
index 1e628ed83..c6d01026c 100644
--- a/lib/puppet/ssl/certificate_factory.rb
+++ b/lib/puppet/ssl/certificate_factory.rb
@@ -66,17 +66,7 @@ module Puppet::SSL::CertificateFactory
inject({}) {|ret, val| ret.merge(val) }
cert.extensions = exts.map do |oid, val|
- val, crit = *val
- val = val.join(', ') unless val.is_a? String
-
- # Enforce the X509v3 rules about subjectAltName being critical:
- # specifically, it SHOULD NOT be critical if we have a subject, which we
- # always do. --daniel 2011-10-18
- crit = false if oid == "subjectAltName"
-
- # val can be either a string, or [string, critical], and this does the
- # right thing regardless of what we get passed.
- ef.create_ext(oid, val, crit)
+ generate_extension(ef, oid, *val)
end
end
@@ -144,5 +134,41 @@ module Puppet::SSL::CertificateFactory
"nsCertType" => "client,email",
}
end
-end
+ # Generate an extension with the given OID, value, and critical state
+ #
+ # @param oid [String] The numeric value or short name of a given OID. X509v3
+ # extensions must be passed by short name or long name, while custom
+ # extensions may be passed by short name, long name, oid numeric OID.
+ # @param ef [OpenSSL::X509::ExtensionFactory] The extension factory to use
+ # when generating the extension.
+ # @param val [String, Array<String>] The extension value.
+ # @param crit [true, false] Whether the given extension is critical, defaults
+ # to false.
+ #
+ # @return [OpenSSL::X509::Extension]
+ #
+ # @api private
+ def self.generate_extension(ef, oid, val, crit = false)
+
+ val = val.join(', ') unless val.is_a? String
+
+ # Enforce the X509v3 rules about subjectAltName being critical:
+ # specifically, it SHOULD NOT be critical if we have a subject, which we
+ # always do. --daniel 2011-10-18
+ crit = false if oid == "subjectAltName"
+
+ if Puppet::SSL::Oids.subtree_of?('id-ce', oid) or Puppet::SSL::Oids.subtree_of?('id-pkix', oid)
+ # Attempt to create a X509v3 certificate extension. Standard certificate
+ # extensions may need access to the associated subject certificate and
+ # issuing certificate, so must be created by the OpenSSL::X509::ExtensionFactory
+ # which provides that context.
+ ef.create_ext(oid, val, crit)
+ else
+ # This is not an X509v3 extension which means that the extension
+ # factory cannot generate it. We need to generate the extension
+ # manually.
+ OpenSSL::X509::Extension.new(oid, val, crit)
+ end
+ end
+end
diff --git a/lib/puppet/ssl/certificate_request.rb b/lib/puppet/ssl/certificate_request.rb
index cc27eebbe..cd68eb34a 100644
--- a/lib/puppet/ssl/certificate_request.rb
+++ b/lib/puppet/ssl/certificate_request.rb
@@ -1,7 +1,30 @@
require 'puppet/ssl/base'
require 'puppet/ssl/certificate_signer'
-# Manage certificate requests.
+# This class creates and manages X509 certificate signing requests.
+#
+# ## CSR attributes
+#
+# CSRs may contain a set of attributes that includes supplementary information
+# about the CSR or information for the signed certificate.
+#
+# PKCS#9/RFC 2985 section 5.4 formally defines the "Challenge password",
+# "Extension request", and "Extended-certificate attributes", but this
+# implementation only handles the "Extension request" attribute. Other
+# attributes may be defined on a CSR, but the RFC doesn't define behavior for
+# any other attributes so we treat them as only informational.
+#
+# ## CSR Extension request attribute
+#
+# CSRs may contain an optional set of extension requests, which allow CSRs to
+# include additional information that may be included in the signed
+# certificate. Any additional information that should be copied from the CSR
+# to the signed certificate MUST be included in this attribute.
+#
+# This behavior is dictated by PKCS#9/RFC 2985 section 5.4.2.
+#
+# @see http://tools.ietf.org/html/rfc2985 "RFC 2985 Section 5.4.2 Extension request"
+#
class Puppet::SSL::CertificateRequest < Puppet::SSL::Base
wraps OpenSSL::X509::Request
@@ -14,7 +37,7 @@ class Puppet::SSL::CertificateRequest < Puppet::SSL::Base
# Try to autosign the CSR.
if ca = Puppet::SSL::CertificateAuthority.instance
- ca.autosign
+ ca.autosign(instance)
end
end
end
@@ -34,7 +57,22 @@ DOC
@ef ||= OpenSSL::X509::ExtensionFactory.new
end
- # How to create a certificate request with our system defaults.
+ # Create a certificate request with our system settings.
+ #
+ # @param key [OpenSSL::X509::Key, Puppet::SSL::Key] The key pair associated
+ # with this CSR.
+ # @param opts [Hash]
+ # @options opts [String] :dns_alt_names A comma separated list of
+ # Subject Alternative Names to include in the CSR extension request.
+ # @options opts [Hash<String, String, Array<String>>] :csr_attributes A hash
+ # of OIDs and values that are either a string or array of strings.
+ # @options opts [Array<String, String>] :extension_requests A hash of
+ # certificate extensions to add to the CSR extReq attribute, excluding
+ # the Subject Alternative Names extension.
+ #
+ # @raise [Puppet::Error] If the generated CSR signature couldn't be verified
+ #
+ # @return [OpenSSL::X509::Request] The generated CSR
def generate(key, options = {})
Puppet.info "Creating a new SSL certificate request for #{name}"
@@ -51,16 +89,12 @@ DOC
csr.subject = OpenSSL::X509::Name.new([["CN", common_name]])
csr.public_key = key.public_key
- if options[:dns_alt_names] then
- names = options[:dns_alt_names].split(/\s*,\s*/).map(&:strip) + [name]
- names = names.sort.uniq.map {|name| "DNS:#{name}" }.join(", ")
- names = extension_factory.create_extension("subjectAltName", names, false)
-
- extReq = OpenSSL::ASN1::Set([OpenSSL::ASN1::Sequence([names])])
+ if options[:csr_attributes]
+ add_csr_attributes(csr, options[:csr_attributes])
+ end
- # We only support the standard request extensions. If you really need
- # msExtReq support, let us know and we can restore them. --daniel 2011-10-10
- csr.add_attribute(OpenSSL::X509::Attribute.new("extReq", extReq))
+ if (ext_req_attribute = extension_request_attribute(options))
+ csr.add_attribute(ext_req_attribute)
end
signer = Puppet::SSL::CertificateSigner.new
@@ -74,72 +108,192 @@ DOC
end
# Return the set of extensions requested on this CSR, in a form designed to
- # be useful to Ruby: a hash. Which, not coincidentally, you can pass
+ # be useful to Ruby: an array of hashes. Which, not coincidentally, you can pass
# successfully to the OpenSSL constructor later, if you want.
+ #
+ # @return [Array<Hash{String => String}>] An array of two or three element
+ # hashes, with key/value pairs for the extension's oid, its value, and
+ # optionally its critical state.
def request_extensions
raise Puppet::Error, "CSR needs content to extract fields" unless @content
# Prefer the standard extReq, but accept the Microsoft specific version as
# a fallback, if the standard version isn't found.
- ext = @content.attributes.find {|x| x.oid == "extReq" } or
- @content.attributes.find {|x| x.oid == "msExtReq" }
- return [] unless ext
-
- # Assert the structure and extract the names into an array of arrays.
- unless ext.value.is_a? OpenSSL::ASN1::Set
- raise Puppet::Error, "In #{ext.oid}, expected Set but found #{ext.value.class}"
- end
-
- unless ext.value.value.is_a? Array
- raise Puppet::Error, "In #{ext.oid}, expected Set[Array] but found #{ext.value.value.class}"
- end
-
- unless ext.value.value.length == 1
- raise Puppet::Error, "In #{ext.oid}, expected Set[Array[...]], but found #{ext.value.value.length} items in the array"
- end
+ attribute = @content.attributes.find {|x| x.oid == "extReq" }
+ attribute ||= @content.attributes.find {|x| x.oid == "msExtReq" }
+ return [] unless attribute
- san = ext.value.value.first
- unless san.is_a? OpenSSL::ASN1::Sequence
- raise Puppet::Error, "In #{ext.oid}, expected Set[Array[Sequence[...]]], but found #{san.class}"
- end
- san = san.value
+ extensions = unpack_extension_request(attribute)
- # OK, now san should be the array of items, validate that...
index = -1
- san.map do |name|
+ extensions.map do |ext_values|
index += 1
-
- unless name.is_a? OpenSSL::ASN1::Sequence
- raise Puppet::Error, "In #{ext.oid}, expected request extension record #{index} to be a Sequence, but found #{name.class}"
- end
- name = name.value
+ context = "#{attribute.oid} extension index #{index}"
# OK, turn that into an extension, to unpack the content. Lovely that
# we have to swap the order of arguments to the underlying method, or
# perhaps that the ASN.1 representation chose to pack them in a
# strange order where the optional component comes *earlier* than the
# fixed component in the sequence.
- case name.length
+ case ext_values.length
when 2
- ev = OpenSSL::X509::Extension.new(name[0].value, name[1].value)
+ ev = OpenSSL::X509::Extension.new(ext_values[0].value, ext_values[1].value)
{ "oid" => ev.oid, "value" => ev.value }
when 3
- ev = OpenSSL::X509::Extension.new(name[0].value, name[2].value, name[1].value)
+ ev = OpenSSL::X509::Extension.new(ext_values[0].value, ext_values[2].value, ext_values[1].value)
{ "oid" => ev.oid, "value" => ev.value, "critical" => ev.critical? }
else
- raise Puppet::Error, "In #{ext.oid}, expected extension record #{index} to have two or three items, but found #{name.length}"
+ raise Puppet::Error, "In #{attribute.oid}, expected extension record #{index} to have two or three items, but found #{ext_values.length}"
end
- end.flatten
+ end
end
def subject_alt_names
@subject_alt_names ||= request_extensions.
- select {|x| x["oid"] = "subjectAltName" }.
+ select {|x| x["oid"] == "subjectAltName" }.
map {|x| x["value"].split(/\s*,\s*/) }.
flatten.
sort.
uniq
end
+
+ # Return all user specified attributes attached to this CSR as a hash. IF an
+ # OID has a single value it is returned as a string, otherwise all values are
+ # returned as an array.
+ #
+ # The format of CSR attributes is specified in PKCS#10/RFC 2986
+ #
+ # @see http://tools.ietf.org/html/rfc2986 "RFC 2986 Certification Request Syntax Specification"
+ #
+ # @api public
+ #
+ # @return [Hash<String, String>]
+ def custom_attributes
+ x509_attributes = @content.attributes.reject do |attr|
+ PRIVATE_CSR_ATTRIBUTES.include? attr.oid
+ end
+
+ x509_attributes.map do |attr|
+ {"oid" => attr.oid, "value" => attr.value.first.value}
+ end
+ end
+
+ private
+
+ # Exclude OIDs that may conflict with how Puppet creates CSRs.
+ #
+ # We only have nominal support for Microsoft extension requests, but since we
+ # ultimately respect that field when looking for extension requests in a CSR
+ # we need to prevent that field from being written to directly.
+ PRIVATE_CSR_ATTRIBUTES = [
+ 'extReq', '1.2.840.113549.1.9.14',
+ 'msExtReq', '1.3.6.1.4.1.311.2.1.14',
+ ]
+
+ def add_csr_attributes(csr, csr_attributes)
+ csr_attributes.each do |oid, value|
+ begin
+ if PRIVATE_CSR_ATTRIBUTES.include? oid
+ raise ArgumentError, "Cannot specify CSR attribute #{oid}: conflicts with internally used CSR attribute"
+ end
+
+ encoded = OpenSSL::ASN1::PrintableString.new(value.to_s)
+
+ attr_set = OpenSSL::ASN1::Set.new([encoded])
+ csr.add_attribute(OpenSSL::X509::Attribute.new(oid, attr_set))
+ Puppet.debug("Added csr attribute: #{oid} => #{attr_set.inspect}")
+ rescue OpenSSL::X509::AttributeError => e
+ raise Puppet::Error, "Cannot create CSR with attribute #{oid}: #{e.message}"
+ end
+ end
+ end
+
+ private
+
+ PRIVATE_EXTENSIONS = [
+ 'subjectAltName', '2.5.29.17',
+ ]
+
+ # @api private
+ def extension_request_attribute(options)
+ extensions = []
+
+ if options[:extension_requests]
+ options[:extension_requests].each_pair do |oid, value|
+ begin
+ if PRIVATE_EXTENSIONS.include? oid
+ raise Puppet::Error, "Cannot specify CSR extension request #{oid}: conflicts with internally used extension request"
+ end
+
+ ext = OpenSSL::X509::Extension.new(oid, value.to_s, false)
+ extensions << ext
+ rescue OpenSSL::X509::ExtensionError => e
+ raise Puppet::Error, "Cannot create CSR with extension request #{oid}: #{e.message}"
+ end
+ end
+ end
+
+ if options[:dns_alt_names]
+ names = options[:dns_alt_names].split(/\s*,\s*/).map(&:strip) + [name]
+ names = names.sort.uniq.map {|name| "DNS:#{name}" }.join(", ")
+ alt_names_ext = extension_factory.create_extension("subjectAltName", names, false)
+
+ extensions << alt_names_ext
+ end
+
+ unless extensions.empty?
+ seq = OpenSSL::ASN1::Sequence(extensions)
+ ext_req = OpenSSL::ASN1::Set([seq])
+ OpenSSL::X509::Attribute.new("extReq", ext_req)
+ end
+ end
+
+ # Unpack the extReq attribute into an array of Extensions.
+ #
+ # The extension request attribute is structured like
+ # `Set[Sequence[Extensions]]` where the outer Set only contains a single
+ # sequence.
+ #
+ # In addition the Ruby implementation of ASN1 requires that all ASN1 values
+ # contain a single value, so Sets and Sequence have to contain an array
+ # that in turn holds the elements. This is why we have to unpack an array
+ # every time we unpack a Set/Seq.
+ #
+ # @see http://tools.ietf.org/html/rfc2985#ref-10 5.4.2 CSR Extension Request structure
+ # @see http://tools.ietf.org/html/rfc5280 4.1 Certificate Extension structure
+ #
+ # @api private
+ #
+ # @param attribute [OpenSSL::X509::Attribute] The X509 extension request
+ #
+ # @return [Array<Array<Object>>] A array of arrays containing the extension
+ # OID the critical state if present, and the extension value.
+ def unpack_extension_request(attribute)
+
+ unless attribute.value.is_a? OpenSSL::ASN1::Set
+ raise Puppet::Error, "In #{attribute.oid}, expected Set but found #{attribute.value.class}"
+ end
+
+ unless attribute.value.value.is_a? Array
+ raise Puppet::Error, "In #{attribute.oid}, expected Set[Array] but found #{attribute.value.value.class}"
+ end
+
+ unless attribute.value.value.size == 1
+ raise Puppet::Error, "In #{attribute.oid}, expected Set[Array] with one value but found #{attribute.value.value.size} elements"
+ end
+
+ unless attribute.value.value.first.is_a? OpenSSL::ASN1::Sequence
+ raise Puppet::Error, "In #{attribute.oid}, expected Set[Array[Sequence[...]]], but found #{extension.class}"
+ end
+
+ unless attribute.value.value.first.value.is_a? Array
+ raise Puppet::Error, "In #{attribute.oid}, expected Set[Array[Sequence[Array[...]]]], but found #{extension.value.class}"
+ end
+
+ extensions = attribute.value.value.first.value
+
+ extensions.map(&:value)
+ end
end
diff --git a/lib/puppet/ssl/certificate_request_attributes.rb b/lib/puppet/ssl/certificate_request_attributes.rb
new file mode 100644
index 000000000..e65b01443
--- /dev/null
+++ b/lib/puppet/ssl/certificate_request_attributes.rb
@@ -0,0 +1,37 @@
+require 'puppet/ssl'
+require 'puppet/util/yaml'
+
+# This class transforms simple key/value pairs into the equivalent ASN1
+# structures. Values may be strings or arrays of strings.
+#
+# @api private
+class Puppet::SSL::CertificateRequestAttributes
+
+ attr_reader :path, :custom_attributes, :extension_requests
+
+ def initialize(path)
+ @path = path
+ @custom_attributes = {}
+ @extension_requests = {}
+ end
+
+ # Attempt to load a yaml file at the given @path.
+ # @return true if we are able to load the file, false otherwise
+ # @raise [Puppet::Error] if there are unexpected attribute keys
+ def load
+ Puppet.info("csr_attributes file loading from #{path}")
+ if Puppet::FileSystem::File.exist?(path)
+ hash = Puppet::Util::Yaml.load_file(path, {})
+ if ! hash.is_a?(Hash)
+ raise Puppet::Error, "invalid CSR attributes, expected instance of Hash, received instance of #{hash.class}"
+ end
+ @custom_attributes = hash.delete('custom_attributes') || {}
+ @extension_requests = hash.delete('extension_requests') || {}
+ if not hash.keys.empty?
+ raise Puppet::Error, "unexpected attributes #{hash.keys.inspect} in #{@path.inspect}"
+ end
+ return true
+ end
+ return false
+ end
+end
diff --git a/lib/puppet/ssl/certificate_revocation_list.rb b/lib/puppet/ssl/certificate_revocation_list.rb
index bb6595477..e19534764 100644
--- a/lib/puppet/ssl/certificate_revocation_list.rb
+++ b/lib/puppet/ssl/certificate_revocation_list.rb
@@ -49,7 +49,7 @@ DOC
Puppet.notice "Revoked certificate with serial #{serial}"
time = Time.now
- add_certitificate_revocation_for(serial, reason, time)
+ add_certificate_revocation_for(serial, reason, time)
update_to_next_crl_number
update_valid_time_range_to_start_at(time)
sign_with(cakey)
@@ -69,7 +69,7 @@ private
@content.extensions = [crl_number_of(0)]
end
- def add_certitificate_revocation_for(serial, reason, time)
+ def add_certificate_revocation_for(serial, reason, time)
revoked = OpenSSL::X509::Revoked.new
revoked.serial = serial
revoked.time = time
diff --git a/lib/puppet/ssl/host.rb b/lib/puppet/ssl/host.rb
index e97ffe6c0..f30b4dee7 100644
--- a/lib/puppet/ssl/host.rb
+++ b/lib/puppet/ssl/host.rb
@@ -4,6 +4,7 @@ require 'puppet/ssl/key'
require 'puppet/ssl/certificate'
require 'puppet/ssl/certificate_request'
require 'puppet/ssl/certificate_revocation_list'
+require 'puppet/ssl/certificate_request_attributes'
# The class that manages all aspects of our SSL certificates --
# private keys, public keys, requests, etc.
@@ -173,6 +174,12 @@ DOC
end
end
+ csr_attributes = Puppet::SSL::CertificateRequestAttributes.new(Puppet[:csr_attributes])
+ if csr_attributes.load
+ options[:csr_attributes] = csr_attributes.custom_attributes
+ options[:extension_requests] = csr_attributes.extension_requests
+ end
+
@certificate_request = CertificateRequest.new(name)
@certificate_request.generate(key.content, options)
begin
@@ -264,14 +271,14 @@ ERROR_STRING
@ssl_store
end
- def to_pson(*args)
+ def to_data_hash
my_cert = Puppet::SSL::Certificate.indirection.find(name)
- pson_hash = { :name => name }
+ result = { :name => name }
my_state = state
- pson_hash[:state] = my_state
- pson_hash[:desired_state] = desired_state if desired_state
+ result[:state] = my_state
+ result[:desired_state] = desired_state if desired_state
thing_to_use = (my_state == 'requested') ? certificate_request : my_cert
@@ -280,7 +287,7 @@ ERROR_STRING
# pson[:fingerprints][:default]
# It appears that we have no internal consumers of this api
# --jeffweiss 30 aug 2012
- pson_hash[:fingerprint] = thing_to_use.fingerprint
+ result[:fingerprint] = thing_to_use.fingerprint
# The above fingerprint doesn't tell us what message digest algorithm was used
# No problem, except that the default is changing between 2.7 and 3.0. Also, as
@@ -289,15 +296,19 @@ ERROR_STRING
# So, when we add the newer fingerprints, we're explicit about the hashing
# algorithm used.
# --jeffweiss 31 july 2012
- pson_hash[:fingerprints] = {}
- pson_hash[:fingerprints][:default] = thing_to_use.fingerprint
+ result[:fingerprints] = {}
+ result[:fingerprints][:default] = thing_to_use.fingerprint
suitable_message_digest_algorithms.each do |md|
- pson_hash[:fingerprints][md] = thing_to_use.fingerprint md
+ result[:fingerprints][md] = thing_to_use.fingerprint md
end
- pson_hash[:dns_alt_names] = thing_to_use.subject_alt_names
+ result[:dns_alt_names] = thing_to_use.subject_alt_names
- pson_hash.to_pson(*args)
+ result
+ end
+
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
# eventually we'll probably want to move this somewhere else or make it
diff --git a/lib/puppet/ssl/inventory.rb b/lib/puppet/ssl/inventory.rb
index c210fdc35..3aae02a65 100644
--- a/lib/puppet/ssl/inventory.rb
+++ b/lib/puppet/ssl/inventory.rb
@@ -8,11 +8,7 @@ class Puppet::SSL::Inventory
# Add a certificate to our inventory.
def add(cert)
cert = cert.content if cert.is_a?(Puppet::SSL::Certificate)
-
- # Create our file, if one does not already exist.
- rebuild unless FileTest.exist?(@path)
-
- Puppet.settings.write(:cert_inventory, "a") do |f|
+ Puppet.settings.setting(:cert_inventory).open("a") do |f|
f.print format(cert)
end
end
@@ -32,16 +28,16 @@ class Puppet::SSL::Inventory
def rebuild
Puppet.notice "Rebuilding inventory file"
- Puppet.settings.write(:cert_inventory) do |f|
- f.print "# Inventory of signed certificates\n# SERIAL NOT_BEFORE NOT_AFTER SUBJECT\n"
+ Puppet.settings.setting(:cert_inventory).open('w') do |f|
+ Puppet::SSL::Certificate.indirection.search("*").each do |cert|
+ f.print format(cert.content)
+ end
end
-
- Puppet::SSL::Certificate.indirection.search("*").each { |cert| add(cert) }
end
# Find the serial number for a given certificate.
def serial(name)
- return nil unless FileTest.exist?(@path)
+ return nil unless Puppet::FileSystem::File.exist?(@path)
File.readlines(@path).each do |line|
next unless line =~ /^(\S+).+\/CN=#{name}$/
diff --git a/lib/puppet/ssl/key.rb b/lib/puppet/ssl/key.rb
index 569fb706d..b64fde544 100644
--- a/lib/puppet/ssl/key.rb
+++ b/lib/puppet/ssl/key.rb
@@ -36,7 +36,7 @@ DOC
end
def password
- return nil unless password_file and FileTest.exist?(password_file)
+ return nil unless password_file and Puppet::FileSystem::File.exist?(password_file)
::File.read(password_file)
end
diff --git a/lib/puppet/ssl/oids.rb b/lib/puppet/ssl/oids.rb
new file mode 100644
index 000000000..6d0a8d0d9
--- /dev/null
+++ b/lib/puppet/ssl/oids.rb
@@ -0,0 +1,78 @@
+require 'puppet/ssl'
+
+# This module defines OIDs for use within Puppet.
+#
+# == ASN.1 Definition
+#
+# The following is the formal definition of OIDs specified in this file.
+#
+# puppetCertExtensions OBJECT IDENTIFIER ::= {iso(1) identified-organization(3)
+# dod(6) internet(1) private(4) enterprise(1) 34380 1}
+#
+# -- the tree under registeredExtensions 'belongs' to puppetlabs
+# -- privateExtensions can be extended by enterprises to suit their own needs
+# registeredExtensions OBJECT IDENTIFIER ::= { puppetCertExtensions 1 }
+# privateExtensions OBJECT IDENTIFIER ::= { puppetCertExtensions 2 }
+#
+# -- subtree of common registered extensions
+# -- The short names for these OIDs are intentionally lowercased and formatted
+# -- since they may be exposed inside the Puppet DSL as variables.
+# pp_uuid OBJECT IDENTIFIER ::= { registeredExtensions 1 }
+# pp_instance_id OBJECT IDENTIFIER ::= { registeredExtensions 2 }
+# pp_image_name OBJECT IDENTIFIER ::= { registeredExtensions 3 }
+# pp_preshared_key OBJECT IDENTIFIER ::= { registeredExtensions 4 }
+#
+# @api private
+module Puppet::SSL::Oids
+
+ PUPPET_OIDS = [
+ ["1.3.6.1.4.1.34380", 'puppetlabs', 'Puppet Labs'],
+ ["1.3.6.1.4.1.34380.1", 'ppCertExt', 'Puppet Certificate Extension'],
+
+ ["1.3.6.1.4.1.34380.1.1", 'ppRegCertExt', 'Puppet Registered Certificate Extension'],
+
+ ["1.3.6.1.4.1.34380.1.1.1", 'pp_uuid', 'Puppet Node UUID'],
+ ["1.3.6.1.4.1.34380.1.1.2", 'pp_instance_id', 'Puppet Node Instance ID'],
+ ["1.3.6.1.4.1.34380.1.1.3", 'pp_image_name', 'Puppet Node Image Name'],
+ ["1.3.6.1.4.1.34380.1.1.4", 'pp_preshared_key', 'Puppet Node Preshared Key'],
+
+ ["1.3.6.1.4.1.34380.1.2", 'ppPrivCertExt', 'Puppet Private Certificate Extension'],
+ ]
+
+ PUPPET_OIDS.each do |oid_defn|
+ OpenSSL::ASN1::ObjectId.register(*oid_defn)
+ end
+
+ # Determine if the first OID contains the second OID
+ #
+ # @param first [String] The containing OID, in dotted form or as the short name
+ # @param second [String] The contained OID, in dotted form or as the short name
+ # @param exclusive [true, false] If an OID should not be considered as a subtree of itself
+ #
+ # @example Comparing two dotted OIDs
+ # Puppet::SSL::Oids.subtree_of?('1.3.6.1', '1.3.6.1.4.1') #=> true
+ # Puppet::SSL::Oids.subtree_of?('1.3.6.1', '1.3.6') #=> false
+ #
+ # @example Comparing an OID short name with a dotted OID
+ # Puppet::SSL::Oids.subtree_of?('IANA', '1.3.6.1.4.1') #=> true
+ # Puppet::SSL::Oids.subtree_of?('1.3.6.1', 'enterprises') #=> true
+ #
+ # @example Comparing an OID against itself
+ # Puppet::SSL::Oids.subtree_of?('IANA', 'IANA') #=> true
+ # Puppet::SSL::Oids.subtree_of?('IANA', 'IANA', true) #=> false
+ #
+ # @return [true, false]
+ def self.subtree_of?(first, second, exclusive = false)
+ first_oid = OpenSSL::ASN1::ObjectId.new(first).oid
+ second_oid = OpenSSL::ASN1::ObjectId.new(second).oid
+
+
+ if exclusive and first_oid == second_oid
+ false
+ else
+ second_oid.index(first_oid) == 0
+ end
+ rescue OpenSSL::ASN1::ASN1Error
+ false
+ end
+end
diff --git a/lib/puppet/ssl/validator.rb b/lib/puppet/ssl/validator.rb
index 18255fb3c..754a74233 100644
--- a/lib/puppet/ssl/validator.rb
+++ b/lib/puppet/ssl/validator.rb
@@ -1,116 +1,60 @@
-require 'puppet/ssl'
require 'openssl'
-module Puppet
-module SSL
-class Validator
- attr_reader :peer_certs
- attr_reader :verify_errors
- attr_reader :ssl_configuration
- ##
- # @param [Hash] opts the options to initialze the instance with.
- #
- # @option opts [Puppet::SSL::Configuration] :ssl_configuration to use for
- # authorizing the peer certificate chain.
- def initialize(opts = {})
- reset!
- @ssl_configuration = opts[:ssl_configuration] or raise ArgumentError, ":ssl_configuration is required"
- end
+# API for certificate verification
+#
+# @api public
+class Puppet::SSL::Validator
- ##
- # reset to the initial state.
- def reset!
- @peer_certs = []
- @verify_errors = []
+ # Factory method for creating an instance of a null/no validator.
+ # This method does not have to be implemented by concrete implementations of this API.
+ #
+ # @return [Puppet::SSL::Validator] produces a validator that performs no validation
+ #
+ # @api public
+ #
+ def self.no_validator()
+ @@no_validator_cache ||= Puppet::SSL::Validator::NoValidator.new()
end
- ##
- # call performs verification of the SSL connection and collection of the
- # certificates for use in constructing the error message if the verification
- # failed. This callback will be executed once for each certificate in a
- # chain being verified.
+ # Factory method for creating an instance of the default Puppet validator.
+ # This method does not have to be implemented by concrete implementations of this API.
#
- # From the [OpenSSL
- # documentation](http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html):
- # The `verify_callback` function is used to control the behaviour when the
- # SSL_VERIFY_PEER flag is set. It must be supplied by the application and
- # receives two arguments: preverify_ok indicates, whether the verification of
- # the certificate in question was passed (preverify_ok=1) or not
- # (preverify_ok=0). x509_ctx is a pointer to the complete context used for
- # the certificate chain verification.
+ # @return [Puppet::SSL::Validator] produces a validator that performs no validation
#
- # See {Puppet::Network::HTTP::Connection} for more information and where this
- # class is intended to be used.
+ # @api public
#
- # @param [Boolean] preverify_ok indicates whether the verification of the
- # certificate in question was passed (preverify_ok=true)
- # @param [OpenSSL::SSL::SSLContext] ssl_context holds the SSLContext for the
- # chain being verified.
- #
- # @return [Boolean] false if the peer is invalid, true otherwise.
- def call(preverify_ok, ssl_context)
- # We must make a copy since the scope of the ssl_context will be lost
- # across invocations of this method.
- current_cert = ssl_context.current_cert
- @peer_certs << Puppet::SSL::Certificate.from_instance(current_cert)
-
- if preverify_ok
- # If we've copied all of the certs in the chain out of the SSL library
- if @peer_certs.length == ssl_context.chain.length
- # (#20027) The peer cert must be issued by a specific authority
- preverify_ok = valid_peer?
- end
- else
- if ssl_context.error_string
- @verify_errors << "#{ssl_context.error_string} for #{current_cert.subject}"
- end
- end
- preverify_ok
- rescue => ex
- @verify_errors << ex.message
- false
+ def self.default_validator()
+ Puppet::SSL::Validator::DefaultValidator.new()
end
- ##
- # Register the instance's call method with the connection.
+ # Array of peer certificates
+ # @return [Array<Puppet::SSL::Certificate>] peer certificates
#
- # @param [Net::HTTP] connection The connection to velidate
+ # @api public
#
- # @return [void]
- def register_verify_callback(connection)
- connection.verify_callback = self
+ def peer_certs
+ raise NotImplementedError, "Concrete class should have implemented this method"
end
- ##
- # Validate the peer certificates against the authorized certificates.
- def valid_peer?
- descending_cert_chain = @peer_certs.reverse.map {|c| c.content }
- authz_ca_certs = ssl_configuration.ca_auth_certificates
-
- if not has_authz_peer_cert(descending_cert_chain, authz_ca_certs)
- msg = "The server presented a SSL certificate chain which does not include a " <<
- "CA listed in the ssl_client_ca_auth file. "
- msg << "Authorized Issuers: #{authz_ca_certs.collect {|c| c.subject}.join(', ')} " <<
- "Peer Chain: #{descending_cert_chain.collect {|c| c.subject}.join(' => ')}"
- @verify_errors << msg
- false
- else
- true
- end
+ # Contains the result of validation
+ # @return [Array<String>, nil] nil, empty Array, or Array with messages
+ #
+ # @api public
+ #
+ def verify_errors
+ raise NotImplementedError, "Concrete class should have implemented this method"
end
- ##
- # checks if the set of peer_certs contains at least one certificate issued
- # by a certificate listed in authz_certs
+ # Registers the connection to validate.
+ #
+ # @param [Net::HTTP] connection The connection to validate
+ #
+ # @return [void]
#
- # @return [Boolean]
- def has_authz_peer_cert(peer_certs, authz_certs)
- peer_certs.any? do |peer_cert|
- authz_certs.any? do |authz_cert|
- peer_cert.verify(authz_cert.public_key)
- end
- end
+ # @api public
+ #
+ def setup_connection(connection)
+ raise NotImplementedError, "Concrete class should have implemented this method"
end
end
-end
-end
+
diff --git a/lib/puppet/ssl/validator/default_validator.rb b/lib/puppet/ssl/validator/default_validator.rb
new file mode 100644
index 000000000..1f238f4c4
--- /dev/null
+++ b/lib/puppet/ssl/validator/default_validator.rb
@@ -0,0 +1,153 @@
+require 'openssl'
+
+# Perform peer certificate verification against the known CA.
+# If there is no CA information known, then no verification is performed
+#
+# @api private
+#
+class Puppet::SSL::Validator::DefaultValidator #< class Puppet::SSL::Validator
+ attr_reader :peer_certs
+ attr_reader :verify_errors
+ attr_reader :ssl_configuration
+
+ # Creates a new DefaultValidator, optionally with an SSL Configuration and SSL Host.
+ #
+ # @param [Puppet::SSL::Configuration] (a default configuration) ssl_configuration the SSL configuration to use
+ # @param [Puppet::SSL::Host] (Puppet::SSL::Host.localhost) the SSL host to use
+ #
+ # @api private
+ #
+ def initialize(
+ ssl_configuration = Puppet::SSL::Configuration.new(
+ Puppet[:localcacert], {
+ :ca_chain_file => Puppet[:ssl_client_ca_chain],
+ :ca_auth_file => Puppet[:ssl_client_ca_auth]
+ }),
+ ssl_host = Puppet::SSL::Host.localhost)
+
+ reset!
+ @ssl_configuration = ssl_configuration
+ @ssl_host = ssl_host
+ end
+
+
+ # Resets this validator to its initial validation state. The ssl configuration is not changed.
+ #
+ # @api private
+ #
+ def reset!
+ @peer_certs = []
+ @verify_errors = []
+ end
+
+ # Performs verification of the SSL connection and collection of the
+ # certificates for use in constructing the error message if the verification
+ # failed. This callback will be executed once for each certificate in a
+ # chain being verified.
+ #
+ # From the [OpenSSL
+ # documentation](http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html):
+ # The `verify_callback` function is used to control the behaviour when the
+ # SSL_VERIFY_PEER flag is set. It must be supplied by the application and
+ # receives two arguments: preverify_ok indicates, whether the verification of
+ # the certificate in question was passed (preverify_ok=1) or not
+ # (preverify_ok=0). x509_ctx is a pointer to the complete context used for
+ # the certificate chain verification.
+ #
+ # See {Puppet::Network::HTTP::Connection} for more information and where this
+ # class is intended to be used.
+ #
+ # @param [Boolean] preverify_ok indicates whether the verification of the
+ # certificate in question was passed (preverify_ok=true)
+ # @param [OpenSSL::SSL::SSLContext] ssl_context holds the SSLContext for the
+ # chain being verified.
+ #
+ # @return [Boolean] false if the peer is invalid, true otherwise.
+ #
+ # @api private
+ #
+ def call(preverify_ok, ssl_context)
+ # We must make a copy since the scope of the ssl_context will be lost
+ # across invocations of this method.
+ current_cert = ssl_context.current_cert
+ @peer_certs << Puppet::SSL::Certificate.from_instance(current_cert)
+
+ if preverify_ok
+ # If we've copied all of the certs in the chain out of the SSL library
+ if @peer_certs.length == ssl_context.chain.length
+ # (#20027) The peer cert must be issued by a specific authority
+ preverify_ok = valid_peer?
+ end
+ else
+ if ssl_context.error_string
+ @verify_errors << "#{ssl_context.error_string} for #{current_cert.subject}"
+ end
+ end
+ preverify_ok
+ rescue => ex
+ @verify_errors << ex.message
+ false
+ end
+
+ # Registers the instance's call method with the connection.
+ #
+ # @param [Net::HTTP] connection The connection to validate
+ #
+ # @return [void]
+ #
+ # @api private
+ #
+ def setup_connection(connection)
+ if ssl_certificates_are_present?
+ connection.cert_store = @ssl_host.ssl_store
+ connection.ca_file = @ssl_configuration.ca_auth_file
+ connection.cert = @ssl_host.certificate.content
+ connection.key = @ssl_host.key.content
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ connection.verify_callback = self
+ else
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+ end
+
+ # Validates the peer certificates against the authorized certificates.
+ #
+ # @api private
+ #
+ def valid_peer?
+ descending_cert_chain = @peer_certs.reverse.map {|c| c.content }
+ authz_ca_certs = ssl_configuration.ca_auth_certificates
+
+ if not has_authz_peer_cert(descending_cert_chain, authz_ca_certs)
+ msg = "The server presented a SSL certificate chain which does not include a " <<
+ "CA listed in the ssl_client_ca_auth file. "
+ msg << "Authorized Issuers: #{authz_ca_certs.collect {|c| c.subject}.join(', ')} " <<
+ "Peer Chain: #{descending_cert_chain.collect {|c| c.subject}.join(' => ')}"
+ @verify_errors << msg
+ false
+ else
+ true
+ end
+ end
+
+ # Checks if the set of peer_certs contains at least one certificate issued
+ # by a certificate listed in authz_certs
+ #
+ # @return [Boolean]
+ #
+ # @api private
+ #
+ def has_authz_peer_cert(peer_certs, authz_certs)
+ peer_certs.any? do |peer_cert|
+ authz_certs.any? do |authz_cert|
+ peer_cert.verify(authz_cert.public_key)
+ end
+ end
+ end
+
+ # @api private
+ #
+ def ssl_certificates_are_present?
+ Puppet::FileSystem::File.exist?(Puppet[:hostcert]) && Puppet::FileSystem::File.exist?(@ssl_configuration.ca_auth_file)
+ end
+end
diff --git a/lib/puppet/ssl/validator/no_validator.rb b/lib/puppet/ssl/validator/no_validator.rb
new file mode 100644
index 000000000..1141b6952
--- /dev/null
+++ b/lib/puppet/ssl/validator/no_validator.rb
@@ -0,0 +1,17 @@
+# Performs no SSL verification
+# @api private
+#
+class Puppet::SSL::Validator::NoValidator < Puppet::SSL::Validator
+
+ def setup_connection(connection)
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
+
+ def peer_certs
+ []
+ end
+
+ def verify_errors
+ []
+ end
+end
diff --git a/lib/puppet/status.rb b/lib/puppet/status.rb
index 9ad55668b..0b26ae22e 100644
--- a/lib/puppet/status.rb
+++ b/lib/puppet/status.rb
@@ -10,6 +10,10 @@ class Puppet::Status
@status = status || {"is_alive" => true}
end
+ def to_data_hash
+ @status
+ end
+
def to_pson(*args)
@status.to_pson
end
diff --git a/lib/puppet/test/test_helper.rb b/lib/puppet/test/test_helper.rb
index c09a1ade2..f80e699e7 100644
--- a/lib/puppet/test/test_helper.rb
+++ b/lib/puppet/test/test_helper.rb
@@ -1,3 +1,5 @@
+require 'puppet/indirector/data_binding/hiera'
+
module Puppet::Test
# This class is intended to provide an API to be used by external projects
# when they are running tests that depend on puppet core. This should
@@ -84,8 +86,11 @@ module Puppet::Test
Puppet::Node::Environment.clear
Puppet::Parser::Functions.reset
Puppet::Application.clear!
+ Puppet::Util::Profiler.clear
Puppet.clear_deprecation_warnings
+
+ Puppet::DataBinding::Hiera.instance_variable_set("@hiera", nil)
end
# Call this method once per test, after execution of each individual test.
diff --git a/lib/puppet/transaction.rb b/lib/puppet/transaction.rb
index a37ec59ad..ab74ed385 100644
--- a/lib/puppet/transaction.rb
+++ b/lib/puppet/transaction.rb
@@ -2,6 +2,7 @@ require 'puppet'
require 'puppet/util/tagging'
require 'puppet/application'
require 'digest/sha1'
+require 'set'
# the class that actually walks our resource/property tree, collects the changes,
# and performs them
@@ -58,7 +59,11 @@ class Puppet::Transaction
continue_while = lambda { !stop_processing? }
+ post_evalable_providers = Set.new
pre_process = lambda do |resource|
+ prov_class = resource.provider.class
+ post_evalable_providers << prov_class if prov_class.respond_to?(:post_resource_eval)
+
prefetch_if_necessary(resource)
# If we generated resources, we don't know what they are now
@@ -90,6 +95,14 @@ class Puppet::Transaction
providerless_types.uniq.each do |type|
Puppet.err "Could not find a suitable provider for #{type}"
end
+
+ post_evalable_providers.each do |provider|
+ begin
+ provider.post_resource_eval
+ rescue => detail
+ Puppet.log_exception(detail, "post_resource_eval failed for provider #{provider}")
+ end
+ end
end
relationship_graph.traverse(:while => continue_while,
diff --git a/lib/puppet/transaction/event.rb b/lib/puppet/transaction/event.rb
index c832665c6..ca4255937 100644
--- a/lib/puppet/transaction/event.rb
+++ b/lib/puppet/transaction/event.rb
@@ -2,17 +2,18 @@ require 'puppet/transaction'
require 'puppet/util/tagging'
require 'puppet/util/logging'
require 'puppet/util/methodhelper'
+require 'puppet/network/format_support'
# A simple struct for storing what happens on the system.
class Puppet::Transaction::Event
include Puppet::Util::MethodHelper
include Puppet::Util::Tagging
include Puppet::Util::Logging
+ include Puppet::Network::FormatSupport
ATTRIBUTES = [:name, :resource, :property, :previous_value, :desired_value, :historical_value, :status, :message, :file, :line, :source_description, :audited, :invalidate_refreshes]
YAML_ATTRIBUTES = %w{@audited @property @previous_value @desired_value @historical_value @message @name @status @time}.map(&:to_sym)
attr_accessor *ATTRIBUTES
- attr_writer :tags
attr_accessor :time
attr_reader :default_log_level
@@ -44,7 +45,7 @@ class Puppet::Transaction::Event
@time = Time.parse(@time) if @time.is_a? String
end
- def to_pson
+ def to_data_hash
{
'audited' => @audited,
'property' => @property,
@@ -55,7 +56,11 @@ class Puppet::Transaction::Event
'name' => @name,
'status' => @status,
'time' => @time.iso8601(9),
- }.to_pson
+ }
+ end
+
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
def property=(prop)
diff --git a/lib/puppet/transaction/report.rb b/lib/puppet/transaction/report.rb
index 92e685f42..0469965cd 100644
--- a/lib/puppet/transaction/report.rb
+++ b/lib/puppet/transaction/report.rb
@@ -216,7 +216,7 @@ class Puppet::Transaction::Report
end
end
- def to_pson
+ def to_data_hash
{
'host' => @host,
'time' => @time.iso8601(9),
@@ -231,7 +231,11 @@ class Puppet::Transaction::Report
'logs' => @logs,
'metrics' => @metrics,
'resource_statuses' => @resource_statuses,
- }.to_pson
+ }
+ end
+
+ def to_pson
+ to_data_hash.to_pson
end
# @return [String] the host name
diff --git a/lib/puppet/transaction/resource_harness.rb b/lib/puppet/transaction/resource_harness.rb
index 99ac751b5..3efcabaf6 100644
--- a/lib/puppet/transaction/resource_harness.rb
+++ b/lib/puppet/transaction/resource_harness.rb
@@ -6,16 +6,50 @@ class Puppet::Transaction::ResourceHarness
attr_reader :transaction
- def allow_changes?(resource)
- if resource.purging? and resource.deleting? and deps = relationship_graph.dependents(resource) \
- and ! deps.empty? and deps.detect { |d| ! d.deleting? }
- deplabel = deps.collect { |r| r.ref }.join(",")
- plurality = deps.length > 1 ? "":"s"
- resource.warning "#{deplabel} still depend#{plurality} on me -- not purging"
- false
- else
- true
+ def initialize(transaction)
+ @transaction = transaction
+ end
+
+ def evaluate(resource)
+ status = Puppet::Resource::Status.new(resource)
+
+ begin
+ context = ResourceApplicationContext.from_resource(resource, status)
+ perform_changes(resource, context)
+
+ if status.changed? && ! resource.noop?
+ cache(resource, :synced, Time.now)
+ resource.flush if resource.respond_to?(:flush)
+ end
+ rescue => detail
+ status.failed_because(detail)
+ ensure
+ status.evaluation_time = Time.now - status.time
+ end
+
+ status
+ end
+
+ def scheduled?(resource)
+ return true if Puppet[:ignoreschedules]
+ return true unless schedule = schedule(resource)
+
+ # We use 'checked' here instead of 'synced' because otherwise we'll
+ # end up checking most resources most times, because they will generally
+ # have been synced a long time ago (e.g., a file only gets updated
+ # once a month on the server and its schedule is daily; the last sync time
+ # will have been a month ago, so we'd end up checking every run).
+ schedule.match?(cached(resource, :checked).to_i)
+ end
+
+ def schedule(resource)
+ unless resource.catalog
+ resource.warning "Cannot schedule without a schedule-containing catalog"
+ return nil
end
+
+ return nil unless name = resource[:schedule]
+ resource.catalog.resource(:schedule, name) || resource.fail("Could not find schedule #{name}")
end
# Used mostly for scheduling and auditing at this point.
@@ -28,153 +62,177 @@ class Puppet::Transaction::ResourceHarness
Puppet::Util::Storage.cache(resource)[name] = value
end
- def perform_changes(resource)
- current_values = resource.retrieve_resource.to_hash
+ private
+ def perform_changes(resource, context)
cache(resource, :checked, Time.now)
return [] if ! allow_changes?(resource)
- historical_values = Puppet::Util::Storage.cache(resource).dup
- desired_values = {}
- resource.properties.each do |property|
- desired_values[property.name] = property.should
- end
- audited_params = (resource[:audit] || []).map { |p| p.to_sym }
- synced_params = []
-
# Record the current state in state.yml.
- audited_params.each do |param|
- cache(resource, param, current_values[param])
+ context.audited_params.each do |param|
+ cache(resource, param, context.current_values[param])
end
- # Update the machine state & create logs/events
- events = []
- ensure_param = resource.parameter(:ensure)
- if desired_values[:ensure] && !ensure_param.safe_insync?(current_values[:ensure])
- events << apply_parameter(ensure_param, current_values[:ensure], audited_params.include?(:ensure), historical_values[:ensure])
- synced_params << :ensure
- elsif current_values[:ensure] != :absent
- work_order = resource.properties # Note: only the resource knows what order to apply changes in
- work_order.each do |param|
- if desired_values[param.name] && !param.safe_insync?(current_values[param.name])
- events << apply_parameter(param, current_values[param.name], audited_params.include?(param.name), historical_values[param.name])
- synced_params << param.name
+ managed_via_ensure = manage_via_ensure_if_possible(resource, context)
+
+ if !managed_via_ensure
+ if context.resource_present?
+ resource.properties.each do |param|
+ sync_if_needed(param, context)
end
+ else
+ resource.debug("Nothing to manage: no ensure and the resource doesn't exist")
end
end
- # Add more events to capture audit results
- audited_params.each do |param_name|
- if historical_values.include?(param_name)
- if historical_values[param_name] != current_values[param_name] && !synced_params.include?(param_name)
- event = create_change_event(resource.parameter(param_name), current_values[param_name], true, historical_values[param_name])
- event.send_log
- events << event
+ capture_audit_events(resource, context)
+ end
+
+ def allow_changes?(resource)
+ if resource.purging? and resource.deleting? and deps = relationship_graph.dependents(resource) \
+ and ! deps.empty? and deps.detect { |d| ! d.deleting? }
+ deplabel = deps.collect { |r| r.ref }.join(",")
+ plurality = deps.length > 1 ? "":"s"
+ resource.warning "#{deplabel} still depend#{plurality} on me -- not purging"
+ false
+ else
+ true
+ end
+ end
+
+ def manage_via_ensure_if_possible(resource, context)
+ ensure_param = resource.parameter(:ensure)
+ if ensure_param && ensure_param.should
+ sync_if_needed(ensure_param, context)
+ else
+ false
+ end
+ end
+
+ def sync_if_needed(param, context)
+ historical_value = context.historical_values[param.name]
+ current_value = context.current_values[param.name]
+ do_audit = context.audited_params.include?(param.name)
+
+ begin
+ if param.should && !param.safe_insync?(current_value)
+ event = create_change_event(param, current_value, historical_value)
+ if do_audit
+ event = audit_event(event, param)
+ end
+
+ brief_audit_message = audit_message(param, do_audit, historical_value, current_value)
+
+ if param.noop
+ noop(event, param, current_value, brief_audit_message)
+ else
+ sync(event, param, current_value, brief_audit_message)
end
+
+ true
else
- resource.property(param_name).notice "audit change: newly-recorded value #{current_values[param_name]}"
+ false
end
- end
+ rescue => detail
+ # Execution will continue on StandardErrors, just store the event
+ Puppet.log_exception(detail)
- events
+ event = create_change_event(param, current_value, historical_value)
+ event.status = "failure"
+ event.message = "change from #{param.is_to_s(current_value)} to #{param.should_to_s(param.should)} failed: #{detail}"
+ false
+ rescue Exception => detail
+ # Execution will halt on Exceptions, they get raised to the application
+ event = create_change_event(param, current_value, historical_value)
+ event.status = "failure"
+ event.message = "change from #{param.is_to_s(current_value)} to #{param.should_to_s(param.should)} failed: #{detail}"
+ raise
+ ensure
+ if event
+ context.record(event)
+ event.send_log
+ context.synced_params << param.name
+ end
+ end
end
- def create_change_event(property, current_value, do_audit, historical_value)
+ def create_change_event(property, current_value, historical_value)
event = property.event
event.previous_value = current_value
event.desired_value = property.should
event.historical_value = historical_value
- if do_audit
- event.audited = true
- event.status = "audit"
- if historical_value != current_value
- event.message = "audit change: previously recorded value #{property.is_to_s(historical_value)} has been changed to #{property.is_to_s(current_value)}"
- end
+ event
+ end
+
+ def audit_event(event, property)
+ event.audited = true
+ event.status = "audit"
+ if event.historical_value != event.previous_value
+ event.message = "audit change: previously recorded value #{property.is_to_s(event.historical_value)} has been changed to #{property.is_to_s(event.previous_value)}"
end
event
end
- def apply_parameter(property, current_value, do_audit, historical_value)
- event = create_change_event(property, current_value, do_audit, historical_value)
-
+ def audit_message(param, do_audit, historical_value, current_value)
if do_audit && historical_value && historical_value != current_value
- brief_audit_message = " (previously recorded value was #{property.is_to_s(historical_value)})"
+ " (previously recorded value was #{param.is_to_s(historical_value)})"
else
- brief_audit_message = ""
+ ""
end
-
- if property.noop
- event.message = "current_value #{property.is_to_s(current_value)}, should be #{property.should_to_s(property.should)} (noop)#{brief_audit_message}"
- event.status = "noop"
- else
- property.sync
- event.message = [ property.change_to_s(current_value, property.should), brief_audit_message ].join
- event.status = "success"
- end
- event
- rescue => detail
- # Execution will continue on StandardErrors, just store the event
- Puppet.log_exception(detail)
- event.status = "failure"
-
- event.message = "change from #{property.is_to_s(current_value)} to #{property.should_to_s(property.should)} failed: #{detail}"
- event
- rescue Exception => detail
- # Execution will halt on Exceptions, they get raised to the application
- event.status = "failure"
- event.message = "change from #{property.is_to_s(current_value)} to #{property.should_to_s(property.should)} failed: #{detail}"
- raise
- ensure
- event.send_log
end
- def evaluate(resource)
- status = Puppet::Resource::Status.new(resource)
+ def noop(event, param, current_value, audit_message)
+ event.message = "current_value #{param.is_to_s(current_value)}, should be #{param.should_to_s(param.should)} (noop)#{audit_message}"
+ event.status = "noop"
+ end
- begin
- perform_changes(resource).each do |event|
- status << event
- end
+ def sync(event, param, current_value, audit_message)
+ param.sync
+ event.message = "#{param.change_to_s(current_value, param.should)}#{audit_message}"
+ event.status = "success"
+ end
- if status.changed? && ! resource.noop?
- cache(resource, :synced, Time.now)
- resource.flush if resource.respond_to?(:flush)
+ def capture_audit_events(resource, context)
+ context.audited_params.each do |param_name|
+ if context.historical_values.include?(param_name)
+ if context.historical_values[param_name] != context.current_values[param_name] && !context.synced_params.include?(param_name)
+ parameter = resource.parameter(param_name)
+ event = audit_event(create_change_event(parameter,
+ context.current_values[param_name],
+ context.historical_values[param_name]),
+ parameter)
+ event.send_log
+ context.record(event)
+ end
+ else
+ resource.property(param_name).notice "audit change: newly-recorded value #{context.current_values[param_name]}"
end
- rescue => detail
- status.failed_because(detail)
- ensure
- status.evaluation_time = Time.now - status.time
end
-
- status
end
- def initialize(transaction)
- @transaction = transaction
- end
-
- def scheduled?(resource)
- return true if Puppet[:ignoreschedules]
- return true unless schedule = schedule(resource)
-
- # We use 'checked' here instead of 'synced' because otherwise we'll
- # end up checking most resources most times, because they will generally
- # have been synced a long time ago (e.g., a file only gets updated
- # once a month on the server and its schedule is daily; the last sync time
- # will have been a month ago, so we'd end up checking every run).
- schedule.match?(cached(resource, :checked).to_i)
- end
+ # @api private
+ ResourceApplicationContext = Struct.new(:current_values,
+ :historical_values,
+ :audited_params,
+ :synced_params,
+ :status) do
+ def self.from_resource(resource, status)
+ ResourceApplicationContext.new(resource.retrieve_resource.to_hash,
+ Puppet::Util::Storage.cache(resource).dup,
+ (resource[:audit] || []).map { |p| p.to_sym },
+ [],
+ status)
+ end
- def schedule(resource)
- unless resource.catalog
- resource.warning "Cannot schedule without a schedule-containing catalog"
- return nil
+ def resource_present?
+ current_values[:ensure] != :absent
end
- return nil unless name = resource[:schedule]
- resource.catalog.resource(:schedule, name) || resource.fail("Could not find schedule #{name}")
+ def record(event)
+ status << event
+ end
end
end
diff --git a/lib/puppet/type.rb b/lib/puppet/type.rb
index 55b4a5fad..941696d28 100644
--- a/lib/puppet/type.rb
+++ b/lib/puppet/type.rb
@@ -692,6 +692,20 @@ class Type
}
end
+ # Return the parameters, metaparams, and properties that have a value or were set by a default. Properties are
+ # included since they are a subclass of parameter.
+ # @return [Array<Puppet::Parameter>] Array of parameter objects ( or subclass thereof )
+ def parameters_with_value
+ self.class.allattrs.collect { |attr| parameter(attr) }.compact
+ end
+
+ # Iterates over all parameters with value currently set.
+ # @yieldparam parameter [Puppet::Parameter] or a subclass thereof
+ # @return [void]
+ def eachparameter
+ parameters_with_value.each { |parameter| yield parameter }
+ end
+
# Creates a transaction event.
# Called by Transaction or by a property.
# Merges the given options with the options `:resource`, `:file`, `:line`, and `:tags`, initialized from
@@ -2278,6 +2292,13 @@ class Type
# @return [Array<Puppet::Parameter>] the validated list/set of attributes
#
def finish
+ # Call post_compile hook on every parameter that implements it. This includes all subclasses
+ # of parameter including, but not limited to, regular parameters, metaparameters, relationship
+ # parameters, and properties.
+ eachparameter do |parameter|
+ parameter.post_compile if parameter.respond_to? :post_compile
+ end
+
# Make sure all of our relationships are valid. Again, must be done
# when the entire catalog is instantiated.
self.class.relationship_params.collect do |klass|
@@ -2297,25 +2318,21 @@ class Type
self[:name]
end
- # Returns the parent of this in the catalog.
- # In case of an erroneous catalog where multiple parents have been produced, the first found (non deterministic)
- # parent is returned.
- # @return [???, nil] WHAT (which types can be the parent of a resource in a catalog?), or nil if there
- # is no catalog.
- #
+ # Returns the parent of this in the catalog. In case of an erroneous catalog
+ # where multiple parents have been produced, the first found (non
+ # deterministic) parent is returned.
+ # @return [Puppet::Type, nil] the
+ # containing resource or nil if there is no catalog or no containing
+ # resource.
def parent
return nil unless catalog
- unless defined?(@parent)
+ @parent ||=
if parents = catalog.adjacent(self, :direction => :in)
- # We should never have more than one parent, so let's just ignore
- # it if we happen to.
- @parent = parents.shift
+ parents.shift
else
- @parent = nil
+ nil
end
- end
- @parent
end
# Returns a reference to this as a string in "Type[name]" format.
diff --git a/lib/puppet/type/augeas.rb b/lib/puppet/type/augeas.rb
index 75c8142f2..930235f0c 100644
--- a/lib/puppet/type/augeas.rb
+++ b/lib/puppet/type/augeas.rb
@@ -96,52 +96,18 @@ Puppet::Type.newtype(:augeas) do
desc "The changes which should be applied to the filesystem. This
can be a command or an array of commands. The following commands are supported:
- `set <PATH> <VALUE>`
- : Sets the value `VALUE` at loction `PATH`
-
-
- `setm <PATH> <SUB> <VALUE>`
- : Sets multiple nodes (matching `SUB` relative to `PATH`) to `VALUE`
-
-
- `rm <PATH>`
- : Removes the node at location `PATH`
-
-
- `remove <PATH>`
- : Synonym for `rm`
-
-
- `clear <PATH>`
- : Sets the node at `PATH` to `NULL`, creating it if needed
-
-
- `clearm <PATH> <SUB>`
- : Sets multiple nodes (matching `SUB` relative to `PATH`) to `NULL`
-
-
- `ins <LABEL> (before|after) <PATH>`
- : Inserts an empty node `LABEL` either before or after `PATH`.
-
-
- `insert <LABEL> <WHERE> <PATH>`
- : Synonym for `ins`
-
-
- `mv <PATH> <OTHER PATH>`
- : Moves a node at `PATH` to the new location `OTHER PATH`
-
-
- `move <PATH> <OTHER PATH>`
- : Synonym for `mv`
-
-
- `defvar <NAME> <PATH>`
- : Sets Augeas variable `$NAME` to `PATH`
-
-
- `defnode <NAME> <PATH> <VALUE>`
- : Sets Augeas variable `$NAME` to `PATH`, creating it with `VALUE` if needed
+ * `set <PATH> <VALUE>` --- Sets the value `VALUE` at loction `PATH`
+ * `setm <PATH> <SUB> <VALUE>` --- Sets multiple nodes (matching `SUB` relative to `PATH`) to `VALUE`
+ * `rm <PATH>` --- Removes the node at location `PATH`
+ * `remove <PATH>` --- Synonym for `rm`
+ * `clear <PATH>` --- Sets the node at `PATH` to `NULL`, creating it if needed
+ * `clearm <PATH> <SUB>` --- Sets multiple nodes (matching `SUB` relative to `PATH`) to `NULL`
+ * `ins <LABEL> (before|after) <PATH>` --- Inserts an empty node `LABEL` either before or after `PATH`.
+ * `insert <LABEL> <WHERE> <PATH>` --- Synonym for `ins`
+ * `mv <PATH> <OTHER PATH>` --- Moves a node at `PATH` to the new location `OTHER PATH`
+ * `move <PATH> <OTHER PATH>` --- Synonym for `mv`
+ * `defvar <NAME> <PATH>` --- Sets Augeas variable `$NAME` to `PATH`
+ * `defnode <NAME> <PATH> <VALUE>` --- Sets Augeas variable `$NAME` to `PATH`, creating it with `VALUE` if needed
If the `context` parameter is set, that value is prepended to any relative `PATH`s."
end
diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb
index 4783ef023..255d1a9d8 100644
--- a/lib/puppet/type/component.rb
+++ b/lib/puppet/type/component.rb
@@ -34,13 +34,7 @@ Puppet::Type.newtype(:component) do
# Component paths are special because they function as containers.
def pathbuilder
if reference.type == "Class"
- # 'main' is the top class, so we want to see '//' instead of
- # its name.
- if reference.title.to_s.downcase == "main"
- myname = ""
- else
- myname = reference.title
- end
+ myname = reference.title
else
myname = reference.to_s
end
diff --git a/lib/puppet/type/cron.rb b/lib/puppet/type/cron.rb
index 198d6c171..198d6c171 100755..100644
--- a/lib/puppet/type/cron.rb
+++ b/lib/puppet/type/cron.rb
diff --git a/lib/puppet/type/exec.rb b/lib/puppet/type/exec.rb
index 85724cc30..d3e83e948 100755..100644
--- a/lib/puppet/type/exec.rb
+++ b/lib/puppet/type/exec.rb
@@ -220,6 +220,18 @@ module Puppet
end
end
+ newparam(:umask, :required_feature => :umask) do
+ desc "Sets the umask to be used while executing this command"
+
+ munge do |value|
+ if value =~ /^0?[0-7]{1,4}$/
+ return value.to_i(8)
+ else
+ raise Puppet::Error, "The umask specification is invalid: #{value.inspect}"
+ end
+ end
+ end
+
newparam(:timeout) do
desc "The maximum time the command should take. If the command takes
longer than the timeout, the command is considered to have failed
@@ -341,7 +353,7 @@ module Puppet
# If the file exists, return false (i.e., don't run the command),
# else return true
def check(value)
- ! FileTest.exists?(value)
+ ! Puppet::FileSystem::File.exist?(value)
end
end
diff --git a/lib/puppet/type/file.rb b/lib/puppet/type/file.rb
index 862ab41ba..df6af7847 100644
--- a/lib/puppet/type/file.rb
+++ b/lib/puppet/type/file.rb
@@ -34,6 +34,9 @@ Puppet::Type.newtype(:file) do
file, the file resource will autorequire them. If Puppet is managing any
parent directories of a file, the file resource will autorequire them."
+ feature :manages_symlinks,
+ "The provider can manage symbolic links."
+
def self.title_patterns
[ [ /^(.*?)\/*\Z/m, [ [ :path ] ] ] ]
end
@@ -54,7 +57,12 @@ Puppet::Type.newtype(:file) do
end
munge do |value|
- ::File.join(::File.split(::File.expand_path(value)))
+ if value.start_with?('//') and ::File.basename(value) == "/"
+ # This is a UNC path pointing to a share, so don't add a trailing slash
+ ::File.expand_path(value)
+ else
+ ::File.join(::File.split(::File.expand_path(value)))
+ end
end
end
@@ -118,15 +126,16 @@ Puppet::Type.newtype(:file) do
end
newparam(:recurse) do
- desc "Whether and how deeply to do recursive
- management. Options are:
+ desc "Whether and how to do recursive file 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
+ directory structure. See `recurselimit` to specify a limit to the
+ recursion depth.
+ * `remote` --- Descends recursively into the remote (source) directory
+ but not the local (destination) directory. Allows copying of
a few files into a directory containing many
unmanaged files without scanning all the local files.
+ This can only be used when a source parameter is specified.
* `false` --- Default of no recursion.
"
@@ -682,7 +691,7 @@ Puppet::Type.newtype(:file) do
end
@stat = begin
- ::File.send(method, self[:path])
+ Puppet::FileSystem::File.new(self[:path]).send(method)
rescue Errno::ENOENT => error
nil
rescue Errno::ENOTDIR => error
@@ -707,7 +716,7 @@ Puppet::Type.newtype(:file) do
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)
+ path = "#{self[:path]}.puppettmp_#{rand(10000)}" while Puppet::FileSystem::File.exist?(path) or Puppet::FileSystem::File.new(path).symlink?
else
path = self[:path]
end
@@ -727,7 +736,7 @@ Puppet::Type.newtype(:file) do
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)
+ Puppet::FileSystem::File.unlink(path) if Puppet::FileSystem::File.exist?(path)
end
end
@@ -777,7 +786,7 @@ Puppet::Type.newtype(:file) do
# @api private
def remove_file(current_type, wanted_type)
debug "Removing existing #{current_type} for replacement with #{wanted_type}"
- ::File.unlink(self[:path])
+ Puppet::FileSystem::File.unlink(self[:path])
stat_needed
true
end
diff --git a/lib/puppet/type/file/checksum.rb b/lib/puppet/type/file/checksum.rb
index 3fd37d455..3fd37d455 100755..100644
--- a/lib/puppet/type/file/checksum.rb
+++ b/lib/puppet/type/file/checksum.rb
diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb
index 5807d1885..6a2eef676 100755..100644
--- a/lib/puppet/type/file/content.rb
+++ b/lib/puppet/type/file/content.rb
@@ -133,6 +133,9 @@ module Puppet
# Make sure we're also managing the checksum property.
def should=(value)
+ # treat the value as a bytestring, in Ruby versions that support it, regardless of the encoding
+ # in which it has been supplied
+ value = value.clone.force_encoding(Encoding::ASCII_8BIT) if value.respond_to?(:force_encoding)
@resource.newattr(:checksum) unless @resource.parameter(:checksum)
super
end
diff --git a/lib/puppet/type/file/ensure.rb b/lib/puppet/type/file/ensure.rb
index de19e10eb..d75c8f6ac 100755..100644
--- a/lib/puppet/type/file/ensure.rb
+++ b/lib/puppet/type/file/ensure.rb
@@ -5,17 +5,35 @@ module Puppet
require 'puppet/util/symbolic_file_mode'
include Puppet::Util::SymbolicFileMode
- desc <<-'EOT'
- Whether to create files that don't currently exist.
- Possible values are `absent`, `present`, `file`, `directory`, and `link`.
- Specifying `present` will match any form of file existence, and
- if the file is missing will create an empty file. Specifying
- `absent` will delete the file (or directory, if `recurse => true` and
- `force => true`). Specifying `link` requires that you also set the `target`
- attribute; note that symlinks cannot be managed on Windows.
-
- If you specify the path to another file as the ensure value, it is
- equivalent to specifying `link` and using that path as the `target`:
+ desc <<-EOT
+ Whether the file should exist, and if so what kind of file it should be.
+ Possible values are `present`, `absent`, `file`, `directory`, and `link`.
+
+ * `present` will accept any form of file existence, and will create a
+ normal file if the file is missing. (The file will have no content
+ unless the `content` or `source` attribute is used.)
+ * `absent` will make sure the file doesn't exist, deleting it
+ if necessary.
+ * `file` will make sure it's a normal file, and enables use of the
+ `content` or `source` attribute.
+ * `directory` will make sure it's a directory, and enables use of the
+ `source`, `recurse`, `recurselimit`, `ignore`, and `purge` attributes.
+ * `link` will make sure the file is a symlink, and **requires** that you
+ also set the `target` attribute. Symlinks are supported on all Posix
+ systems and on Windows Vista / 2008 and higher. On Windows, managing
+ symlinks requires puppet agent's user account to have the "Create
+ Symbolic Links" privilege; this can be configured in the "User Rights
+ Assignment" section in the Windows policy editor. By default, puppet
+ agent runs as the Administrator account, which does have this privilege.
+
+ Puppet avoids destroying directories unless the `force` attribute is set
+ to `true`. This means that if a file is currently a directory, setting
+ `ensure` to anything but `directory` or `present` will cause Puppet to
+ skip managing the resource and log either a notice or an error.
+
+ There is one other non-standard value for `ensure`. If you specify the
+ path to another file as the ensure value, it is equivalent to specifying
+ `link` and using that path as the `target`:
# Equivalent resources:
@@ -36,7 +54,7 @@ module Puppet
nodefault
newvalue(:absent) do
- File.unlink(@resource[:path])
+ Puppet::FileSystem::File.unlink(@resource[:path])
end
aliasvalue(:false, :absent)
@@ -61,7 +79,7 @@ module Puppet
newvalue(:directory, :event => :directory_created) do
mode = @resource.should(:mode)
parent = File.dirname(@resource[:path])
- unless FileTest.exists? parent
+ unless Puppet::FileSystem::File.exist? parent
raise Puppet::Error,
"Cannot create #{@resource[:path]}; parent directory #{parent} does not exist"
end
@@ -77,7 +95,7 @@ module Puppet
end
- newvalue(:link, :event => :link_created) do
+ newvalue(:link, :event => :link_created, :required_features => :manages_symlinks) do
fail "Cannot create a symlink without a target" unless property = resource.property(:target)
property.retrieve
property.mklink
@@ -121,7 +139,7 @@ module Puppet
def check
basedir = File.dirname(@resource[:path])
- if ! FileTest.exists?(basedir)
+ if ! Puppet::FileSystem::File.exist?(basedir)
raise Puppet::Error,
"Can not create #{@resource.title}; parent directory does not exist"
elsif ! FileTest.directory?(basedir)
diff --git a/lib/puppet/type/file/group.rb b/lib/puppet/type/file/group.rb
index a1ae66518..a1ae66518 100755..100644
--- a/lib/puppet/type/file/group.rb
+++ b/lib/puppet/type/file/group.rb
diff --git a/lib/puppet/type/file/mode.rb b/lib/puppet/type/file/mode.rb
index b6b4becf2..682e744cd 100755..100644
--- a/lib/puppet/type/file/mode.rb
+++ b/lib/puppet/type/file/mode.rb
@@ -39,8 +39,12 @@ module Puppet
* g (group's current permissions)
* o (other's current permissions)
- Thus, mode `0664` could be represented symbolically as either `a=r,ug+w` or
- `ug=rw,o=r`. See the manual page for GNU or BSD `chmod` for more details
+ Thus, mode `0664` could be represented symbolically as either `a=r,ug+w`
+ or `ug=rw,o=r`. However, symbolic modes are more expressive than numeric
+ modes: a mode only affects the specified bits, so `mode => 'ug+w'` will
+ set the user and group write bits, without affecting any other bits.
+
+ See the manual page for GNU or BSD `chmod` for more details
on numeric and symbolic modes.
On Windows, permissions are translated as follows:
diff --git a/lib/puppet/type/file/owner.rb b/lib/puppet/type/file/owner.rb
index 3b61b400c..3b61b400c 100755..100644
--- a/lib/puppet/type/file/owner.rb
+++ b/lib/puppet/type/file/owner.rb
diff --git a/lib/puppet/type/file/source.rb b/lib/puppet/type/file/source.rb
index 000636b6b..7f88e692a 100755..100644
--- a/lib/puppet/type/file/source.rb
+++ b/lib/puppet/type/file/source.rb
@@ -1,4 +1,3 @@
-
require 'puppet/file_serving/content'
require 'puppet/file_serving/metadata'
@@ -85,7 +84,7 @@ module Puppet
def change_to_s(currentvalue, newvalue)
# newvalue = "{md5}#{@metadata.checksum}"
- if @resource.property(:ensure).retrieve == :absent
+ if resource.property(:ensure).retrieve == :absent
return "creating from source #{metadata.source} with contents #{metadata.checksum}"
else
return "replacing from source #{metadata.source} with contents #{metadata.checksum}"
@@ -111,28 +110,48 @@ module Puppet
def copy_source_values
devfail "Somehow got asked to copy source values without any metadata" unless metadata
+ # conditionally copy :checksum
+ if metadata.ftype != "directory" && !(metadata.ftype == "link" && metadata.links == :manage)
+ copy_source_value(:checksum)
+ end
+
# Take each of the stats and set them as states on the local file
# if a value has not already been provided.
- [:owner, :mode, :group, :checksum].each do |metadata_method|
- param_name = (metadata_method == :checksum) ? :content : metadata_method
+ [:owner, :mode, :group].each do |metadata_method|
next if metadata_method == :owner and !Puppet.features.root?
- next if metadata_method == :checksum and metadata.ftype == "directory"
- next if metadata_method == :checksum and metadata.ftype == "link" and metadata.links == :manage
+ next if metadata_method == :group and !Puppet.features.root?
if Puppet.features.microsoft_windows?
- next if [:owner, :group].include?(metadata_method) and !local?
+ # Warn on Windows if source permissions are being used and the file resource
+ # does not have mode owner and group all set (which would take precedence).
+ if [:use, :use_when_creating].include?(resource[:source_permissions]) &&
+ (resource[:owner] == nil || resource[:group] == nil || resource[:mode] == nil)
+
+ warning = "Copying %s from the source" <<
+ " file on Windows is deprecated;" <<
+ " use source_permissions => ignore."
+ Puppet.deprecation_warning(warning % 'owner/mode/group')
+ resource.debug(warning % metadata_method.to_s)
+ end
+ # But never try to copy remote owner/group on Windows
+ next if [:owner, :group].include?(metadata_method) && !local?
end
- if resource[param_name].nil? or resource[param_name] == :absent
- resource[param_name] = metadata.send(metadata_method)
+ case resource[:source_permissions]
+ when :ignore
+ next
+ when :use_when_creating
+ next if Puppet::FileSystem::File.exist?(resource[:path])
end
+
+ copy_source_value(metadata_method)
end
if resource[:ensure] == :absent
# We know all we need to
elsif metadata.ftype != "link"
resource[:ensure] = metadata.ftype
- elsif @resource[:links] == :follow
+ elsif resource[:links] == :follow
resource[:ensure] = :present
else
resource[:ensure] = "link"
@@ -140,10 +159,6 @@ module Puppet
end
end
- def found?
- ! (metadata.nil? or metadata.ftype.nil?)
- end
-
attr_writer :metadata
# Provide, and retrieve if necessary, the metadata for this file. Fail
@@ -195,5 +210,41 @@ module Puppet
def uri
@uri ||= URI.parse(URI.escape(metadata.source))
end
+
+ private
+ def found?
+ ! (metadata.nil? or metadata.ftype.nil?)
+ end
+
+ def copy_source_value(metadata_method)
+ param_name = (metadata_method == :checksum) ? :content : metadata_method
+ if resource[param_name].nil? or resource[param_name] == :absent
+ resource[param_name] = metadata.send(metadata_method)
+ end
+ end
+ end
+
+ Puppet::Type.type(:file).newparam(:source_permissions) do
+ desc <<-'EOT'
+ Whether (and how) Puppet should copy owner, group, and mode permissions from
+ the `source` to `file` resources when the permissions are not explicitly
+ specified. (In all cases, explicit permissions will take precedence.)
+ Valid values are `use`, `use_when_creating`, and `ignore`:
+
+ * `use` (the default) will cause Puppet to apply the owner, group,
+ and mode from the `source` to any files it is managing.
+ * `use_when_creating` will only apply the owner, group, and mode from the
+ `source` when creating a file; existing files will not have their permissions
+ overwritten.
+ * `ignore` will never apply the owner, group, or mode from the `source` when
+ managing a file. When creating new files without explicit permissions,
+ the permissions they receive will depend on platform-specific behavior.
+ On POSIX, Puppet will use the umask of the user it is running as. On
+ Windows, Puppet will use the default DACL associated with the user it is
+ running as.
+ EOT
+
+ defaultto :use
+ newvalues(:use, :use_when_creating, :ignore)
end
end
diff --git a/lib/puppet/type/file/target.rb b/lib/puppet/type/file/target.rb
index e1dbdeae2..08a2a97df 100644
--- a/lib/puppet/type/file/target.rb
+++ b/lib/puppet/type/file/target.rb
@@ -32,7 +32,7 @@ module Puppet
# Create our link.
def mklink
- raise Puppet::Error, "Cannot symlink on Microsoft Windows" if Puppet.features.microsoft_windows?
+ raise Puppet::Error, "Cannot symlink on this platform version" if !provider.feature?(:manages_symlinks)
target = self.should
@@ -40,17 +40,17 @@ module Puppet
# it doesn't determine what's removed.
@resource.remove_existing(target)
- raise Puppet::Error, "Could not remove existing file" if FileTest.exists?(@resource[:path])
+ raise Puppet::Error, "Could not remove existing file" if Puppet::FileSystem::File.exist?(@resource[:path])
Dir.chdir(File.dirname(@resource[:path])) do
Puppet::Util::SUIDManager.asuser(@resource.asuser) do
mode = @resource.should(:mode)
if mode
Puppet::Util.withumask(000) do
- File.symlink(target, @resource[:path])
+ Puppet::FileSystem::File.new(target).symlink(@resource[:path])
end
else
- File.symlink(target, @resource[:path])
+ Puppet::FileSystem::File.new(target).symlink(@resource[:path])
end
end
@@ -63,7 +63,7 @@ module Puppet
def insync?(currentvalue)
if [:nochange, :notlink].include?(self.should) or @resource.recurse?
return true
- elsif ! @resource.replace? and File.exists?(@resource[:path])
+ elsif ! @resource.replace? and Puppet::FileSystem::File.exist?(@resource[:path])
return true
else
return super(currentvalue)
@@ -74,7 +74,7 @@ module Puppet
def retrieve
if stat = @resource.stat
if stat.ftype == "link"
- return File.readlink(@resource[:path])
+ return Puppet::FileSystem::File.new(@resource[:path]).readlink
else
return :notlink
end
diff --git a/lib/puppet/type/file/type.rb b/lib/puppet/type/file/type.rb
index 38f301573..38f301573 100755..100644
--- a/lib/puppet/type/file/type.rb
+++ b/lib/puppet/type/file/type.rb
diff --git a/lib/puppet/type/filebucket.rb b/lib/puppet/type/filebucket.rb
index cbb551464..cbb551464 100755..100644
--- a/lib/puppet/type/filebucket.rb
+++ b/lib/puppet/type/filebucket.rb
diff --git a/lib/puppet/type/group.rb b/lib/puppet/type/group.rb
index a66a29452..cf9eb2382 100755..100644
--- a/lib/puppet/type/group.rb
+++ b/lib/puppet/type/group.rb
@@ -87,6 +87,24 @@ module Puppet
newvalue = newvalue.join(",")
super(currentvalue, newvalue)
end
+
+ def insync?(current)
+ if provider.respond_to?(:members_insync?)
+ return provider.members_insync?(current, @should)
+ end
+
+ super(current)
+ end
+
+ def is_to_s(currentvalue)
+ if provider.respond_to?(:members_to_s)
+ currentvalue = '' if currentvalue.nil?
+ return provider.members_to_s(currentvalue.split(','))
+ end
+
+ super(currentvalue)
+ end
+ alias :should_to_s :is_to_s
end
newparam(:auth_membership) do
diff --git a/lib/puppet/type/host.rb b/lib/puppet/type/host.rb
index d63b37d1a..d63b37d1a 100755..100644
--- a/lib/puppet/type/host.rb
+++ b/lib/puppet/type/host.rb
diff --git a/lib/puppet/type/k5login.rb b/lib/puppet/type/k5login.rb
index b2fff2793..a87b3e7d8 100644
--- a/lib/puppet/type/k5login.rb
+++ b/lib/puppet/type/k5login.rb
@@ -37,7 +37,7 @@ Puppet::Type.newtype(:k5login) do
# Does this file exist?
def exists?
- File.exists?(@resource[:name])
+ Puppet::FileSystem::File.exist?(@resource[:name])
end
# create the file
@@ -51,12 +51,12 @@ Puppet::Type.newtype(:k5login) do
# remove the file
def destroy
- File.unlink(@resource[:name])
+ Puppet::FileSystem::File.unlink(@resource[:name])
end
# Return the principals
def principals(dummy_argument=:work_arround_for_ruby_GC_bug)
- if File.exists?(@resource[:name])
+ if Puppet::FileSystem::File.exist?(@resource[:name])
File.readlines(@resource[:name]).collect { |line| line.chomp }
else
:absent
@@ -70,7 +70,7 @@ Puppet::Type.newtype(:k5login) do
# Return the mode as an octal string, not as an integer
def mode
- "%o" % (File.stat(@resource[:name]).mode & 007777)
+ "%o" % (Puppet::FileSystem::File.new(@resource[:name]).stat.mode & 007777)
end
# Set the file mode, converting from a string to an integer.
diff --git a/lib/puppet/type/mailalias.rb b/lib/puppet/type/mailalias.rb
index ce7ca790b..ce7ca790b 100755..100644
--- a/lib/puppet/type/mailalias.rb
+++ b/lib/puppet/type/mailalias.rb
diff --git a/lib/puppet/type/maillist.rb b/lib/puppet/type/maillist.rb
index 4e0542c83..4e0542c83 100755..100644
--- a/lib/puppet/type/maillist.rb
+++ b/lib/puppet/type/maillist.rb
diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb
index 27624e31b..acad4927d 100755..100644
--- a/lib/puppet/type/mount.rb
+++ b/lib/puppet/type/mount.rb
@@ -8,7 +8,11 @@ module Puppet
on the value of the 'ensure' parameter.
Note that if a `mount` receives an event from another resource,
- it will try to remount the filesystems if `ensure` is set to `mounted`."
+ it will try to remount the filesystems if `ensure` is set to `mounted`.
+
+ **Autorequires:** If Puppet is managing any parents of a mount resource ---
+ that is, other mount points higher up in the filesystem --- the child
+ mount will autorequire them."
feature :refreshable, "The provider can remount the filesystem.",
:methods => [:remount]
@@ -268,5 +272,15 @@ module Puppet
return property.value
end
end
+
+ # Ensure that mounts higher up in the filesystem are mounted first
+ autorequire(:mount) do
+ dependencies = []
+ Pathname.new(@parameters[:name].value).ascend do |parent|
+ dependencies.unshift parent.to_s
+ end
+ dependencies[0..-2]
+ end
+
end
end
diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb
index 5500f4499..e7ea1125b 100644
--- a/lib/puppet/type/package.rb
+++ b/lib/puppet/type/package.rb
@@ -58,7 +58,7 @@ module Puppet
retrieve by specifying a version number or `latest` as the ensure
value. On packaging systems that manage configuration files separately
from "normal" system files, you can uninstall config files by
- specifying `purged` as the ensure value.
+ specifying `purged` as the ensure value. This defaults to `installed`.
EOT
attr_accessor :latest
@@ -225,6 +225,12 @@ module Puppet
"
isnamevar
+
+ validate do |value|
+ if !value.is_a?(String)
+ raise ArgumentError, "Name must be a String not #{value.class}"
+ end
+ end
end
newparam(:source) do
diff --git a/lib/puppet/type/port.rb b/lib/puppet/type/port.rb
index e19988515..e19988515 100755..100644
--- a/lib/puppet/type/port.rb
+++ b/lib/puppet/type/port.rb
diff --git a/lib/puppet/type/schedule.rb b/lib/puppet/type/schedule.rb
index e1586cbdb..d67831e89 100755..100644
--- a/lib/puppet/type/schedule.rb
+++ b/lib/puppet/type/schedule.rb
@@ -356,10 +356,10 @@ module Puppet
of the range, not necessarily the day that it is when it matches.
For example, consider this schedule:
- schedule { 'maintenance_window':
- range => '22:00 - 04:00',
- weekday => 'Saturday',
- }
+ schedule { 'maintenance_window':
+ range => '22:00 - 04:00',
+ weekday => 'Saturday',
+ }
This will match at 11 PM on Saturday and 2 AM on Sunday, but not
at 2 AM on Saturday.
@@ -418,6 +418,11 @@ module Puppet
def self.mkdefaultschedules
result = []
+ unless Puppet[:default_schedules]
+ Puppet.debug "Not creating default schedules: default_schedules is false"
+ return result
+ end
+
Puppet.debug "Creating default schedules"
result << self.new(
diff --git a/lib/puppet/type/service.rb b/lib/puppet/type/service.rb
index 0a74649cf..e4876664e 100644
--- a/lib/puppet/type/service.rb
+++ b/lib/puppet/type/service.rb
@@ -180,7 +180,7 @@ module Puppet
automatically, usually by looking for the service in the process
table.
- [lsb-exit-codes]: http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/iniscrptact.html"
+ [lsb-exit-codes]: http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html"
end
newparam(:stop) do
diff --git a/lib/puppet/type/sshkey.rb b/lib/puppet/type/sshkey.rb
index 41948ed98..41948ed98 100755..100644
--- a/lib/puppet/type/sshkey.rb
+++ b/lib/puppet/type/sshkey.rb
diff --git a/lib/puppet/type/tidy.rb b/lib/puppet/type/tidy.rb
index 8297cf938..83ac3322d 100755..100644
--- a/lib/puppet/type/tidy.rb
+++ b/lib/puppet/type/tidy.rb
@@ -312,7 +312,7 @@ Puppet::Type.newtype(:tidy) do
def stat(path)
begin
- ::File.lstat(path)
+ Puppet::FileSystem::File.new(path).lstat
rescue Errno::ENOENT => error
info "File does not exist"
return nil
diff --git a/lib/puppet/type/user.rb b/lib/puppet/type/user.rb
index 9ec6e922f..d0ca0f78c 100755..100644
--- a/lib/puppet/type/user.rb
+++ b/lib/puppet/type/user.rb
@@ -124,8 +124,9 @@ module Puppet
newproperty(:gid) do
desc "The user's primary group. Can be specified numerically or by name.
- Note that users on Windows systems do not have a primary group; manage groups
- with the `groups` attribute instead."
+ This attribute is not supported on Windows systems; use the `groups`
+ attribute instead. (On Windows, designating a primary group is only
+ meaningful for domain accounts, which Puppet does not currently manage.)"
munge do |value|
if value.is_a?(String) and value =~ /^[-0-9]+$/
@@ -163,6 +164,9 @@ module Puppet
newproperty(:comment) do
desc "A description of the user. Generally the user's full name."
+ munge do |v|
+ v.respond_to?(:encode) ? v.encode(Encoding::ASCII_8BIT) : v
+ end
end
newproperty(:shell) do
diff --git a/lib/puppet/type/yumrepo.rb b/lib/puppet/type/yumrepo.rb
index bd52121c9..f71736045 100644
--- a/lib/puppet/type/yumrepo.rb
+++ b/lib/puppet/type/yumrepo.rb
@@ -170,7 +170,7 @@ module Puppet
unless Puppet[:noop]
target_mode = 0644 # FIXME: should be configurable
inifile.each_file do |file|
- current_mode = ::File.stat(file).mode & 0777
+ current_mode = Puppet::FileSystem::File.new(file).stat.mode & 0777
unless current_mode == target_mode
Puppet::info "changing mode of #{file} from %03o to %03o" % [current_mode, target_mode]
::File.chmod(target_mode, file)
@@ -332,7 +332,7 @@ module Puppet
end
newproperty(:cost, :parent => Puppet::IniProperty) do
- desc "Cost of this repository.\n#{ABSENT_DOC}"
+ desc "Cost of this repository. #{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(%r{\d+}) { }
end
@@ -364,28 +364,30 @@ module Puppet
newproperty(:sslcacert, :parent => Puppet::IniProperty) do
desc "Path to the directory containing the databases of the
- certificate authorities yum should use to verify SSL certificates.\n#{ABSENT_DOC}"
+ certificate authorities yum should use to verify SSL certificates.
+ #{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(/.*/) { }
end
newproperty(:sslverify, :parent => Puppet::IniProperty) do
desc "Should yum verify SSL certificates/hosts at all.
- Possible values are 'True' or 'False'.\n#{ABSENT_DOC}"
+ Possible values are 'True' or 'False'.
+ #{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(%r(True|False)) { }
end
newproperty(:sslclientcert, :parent => Puppet::IniProperty) do
desc "Path to the SSL client certificate yum should use to connect
- to repos/remote sites.\n#{ABSENT_DOC}"
+ to repos/remote sites. #{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(/.*/) { }
end
newproperty(:sslclientkey, :parent => Puppet::IniProperty) do
desc "Path to the SSL client key yum should use to connect
- to repos/remote sites.\n#{ABSENT_DOC}"
+ to repos/remote sites. #{ABSENT_DOC}"
newvalue(:absent) { self.should = :absent }
newvalue(/.*/) { }
end
diff --git a/lib/puppet/type/zpool.rb b/lib/puppet/type/zpool.rb
index 043deecc4..043deecc4 100755..100644
--- a/lib/puppet/type/zpool.rb
+++ b/lib/puppet/type/zpool.rb
diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb
index c1a82340e..4c04f37aa 100644
--- a/lib/puppet/util.rb
+++ b/lib/puppet/util.rb
@@ -1,12 +1,9 @@
# A module to collect utility functions.
require 'English'
-require 'puppet/external/lock'
require 'puppet/error'
require 'puppet/util/execution_stub'
require 'uri'
-require 'sync'
-require 'monitor'
require 'tempfile'
require 'pathname'
require 'ostruct'
@@ -26,9 +23,6 @@ module Util
extend Puppet::Util::SymbolicFileMode
- @@sync_objects = {}.extend MonitorMixin
-
-
def self.activerecord_version
if (defined?(::ActiveRecord) and defined?(::ActiveRecord::VERSION) and defined?(::ActiveRecord::VERSION::MAJOR) and defined?(::ActiveRecord::VERSION::MINOR))
([::ActiveRecord::VERSION::MAJOR, ::ActiveRecord::VERSION::MINOR].join('.').to_f)
@@ -67,20 +61,6 @@ module Util
end
- def self.synchronize_on(x,type)
- sync_object,users = 0,1
- begin
- @@sync_objects.synchronize {
- (@@sync_objects[x] ||= [Sync.new,0])[users] += 1
- }
- @@sync_objects[x][sync_object].synchronize(type) { yield }
- ensure
- @@sync_objects.synchronize {
- @@sync_objects.delete(x) unless (@@sync_objects[x][users] -= 1) > 0
- }
- end
- end
-
# Change the process to a different user
def self.chuser
if group = Puppet[:group]
@@ -154,7 +134,6 @@ module Util
end
end
-
def benchmark(*args)
msg = args.pop
level = args.pop
@@ -187,6 +166,7 @@ module Util
yield
end
end
+ module_function :benchmark
# Resolve a path for an executable to the absolute path. This tries to behave
# in the same manner as the unix `which` command and uses the `PATH`
@@ -270,7 +250,7 @@ module Util
if Puppet.features.microsoft_windows?
path = path.gsub(/\\/, '/')
- if unc = /^\/\/([^\/]+)(\/[^\/]+)/.match(path)
+ if unc = /^\/\/([^\/]+)(\/.+)/.match(path)
params[:host] = unc[1]
path = unc[2]
elsif path =~ /^[a-z]:\//i
@@ -320,13 +300,6 @@ module Util
end
module_function :safe_posix_fork
- # Create an exclusive lock.
- def threadlock(resource, type = Sync::EX)
- Puppet::Util.synchronize_on(resource,type) { yield }
- end
-
- module_function :benchmark
-
def memory
unless defined?(@pmap)
@pmap = which('pmap')
@@ -361,6 +334,7 @@ module Util
# Because IO#binread is only available in 1.9
def binread(file)
+ Puppet.deprecation_warning("Puppet::Util.binread is deprecated. Read the file without this method as it will be removed in a future version.")
File.open(file, 'rb') { |f| f.read }
end
module_function :binread
@@ -467,7 +441,7 @@ module Util
# This might race, but there are enough possible cases that there
# isn't a good, solid "better" way to do this, and the next call
# should fail in the same way anyhow.
- raise if have_retried or File.exist?(file)
+ raise if have_retried or Puppet::FileSystem::File.exist?(file)
have_retried = true
# OK, so, we can't replace a file that doesn't exist, so let us put
@@ -499,7 +473,6 @@ module Util
end
module_function :replace_file
-
# Executes a block of code, wrapped with some special exception handling. Causes the ruby interpreter to
# exit if the block throws an exception.
#
diff --git a/lib/puppet/util/adsi.rb b/lib/puppet/util/adsi.rb
index a4b02d537..98639eabd 100644
--- a/lib/puppet/util/adsi.rb
+++ b/lib/puppet/util/adsi.rb
@@ -41,6 +41,11 @@ module Puppet::Util::ADSI
"winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2"
end
+ def sid_uri(sid)
+ raise Puppet::Error.new( "Must use a valid SID object" ) if !sid.kind_of?(Win32::Security::SID)
+ "WinNT://#{sid.to_s}"
+ end
+
def uri(resource_name, resource_type, host = '.')
"#{computer_uri(host)}/#{resource_name},#{resource_type}"
end
@@ -64,22 +69,40 @@ module Puppet::Util::ADSI
extend Enumerable
attr_accessor :native_user
- attr_reader :name
+ attr_reader :name, :sid
def initialize(name, native_user = nil)
@name = name
@native_user = native_user
end
+ def self.parse_name(name)
+ if name =~ /\//
+ raise Puppet::Error.new( "Value must be in DOMAIN\\user style syntax" )
+ end
+
+ matches = name.scan(/((.*)\\)?(.*)/)
+ domain = matches[0][1] || '.'
+ account = matches[0][2]
+
+ return account, domain
+ end
+
def native_user
- @native_user ||= Puppet::Util::ADSI.connect(uri)
+ @native_user ||= Puppet::Util::ADSI.connect(self.class.uri(*self.class.parse_name(@name)))
+ end
+
+ def sid
+ @sid ||= Puppet::Util::Windows::Security.octet_string_to_sid_object(native_user.objectSID)
end
def self.uri(name, host = '.')
+ host = '.' if ['NT AUTHORITY', 'BUILTIN', Socket.gethostname].include?(host)
+
Puppet::Util::ADSI.uri(name, 'user', host)
end
def uri
- self.class.uri(name)
+ self.class.uri(sid.account, sid.domain)
end
def self.logon(name, password)
@@ -131,14 +154,14 @@ module Puppet::Util::ADSI
def add_to_groups(*group_names)
group_names.each do |group_name|
- Puppet::Util::ADSI::Group.new(group_name).add_member(@name)
+ Puppet::Util::ADSI::Group.new(group_name).add_member_sids(sid)
end
end
alias add_to_group add_to_groups
def remove_from_groups(*group_names)
group_names.each do |group_name|
- Puppet::Util::ADSI::Group.new(group_name).remove_member(@name)
+ Puppet::Util::ADSI::Group.new(group_name).remove_member_sids(sid)
end
end
alias remove_from_group remove_from_groups
@@ -167,7 +190,7 @@ module Puppet::Util::ADSI
end
def self.exists?(name)
- Puppet::Util::ADSI::connectable?(User.uri(name))
+ Puppet::Util::ADSI::connectable?(User.uri(*User.parse_name(name)))
end
def self.delete(name)
@@ -175,7 +198,7 @@ module Puppet::Util::ADSI
end
def self.each(&block)
- wql = Puppet::Util::ADSI.execquery("select name from win32_useraccount")
+ wql = Puppet::Util::ADSI.execquery('select name from win32_useraccount where localaccount = "TRUE"')
users = []
wql.each do |u|
@@ -233,20 +256,44 @@ module Puppet::Util::ADSI
self
end
- def add_members(*names)
- names.each do |name|
- native_group.Add(Puppet::Util::ADSI::User.uri(name, Puppet::Util::ADSI.computer_name))
+ def self.name_sid_hash(names)
+ return [] if names.nil? or names.empty?
+
+ sids = names.map do |name|
+ sid = Puppet::Util::Windows::Security.name_to_sid_object(name)
+ raise Puppet::Error.new( "Could not resolve username: #{name}" ) if !sid
+ [sid.to_s, sid]
end
+
+ Hash[ sids ]
+ end
+
+ def add_members(*names)
+ Puppet.deprecation_warning('Puppet::Util::ADSI::Group#add_members is deprecated; please use Puppet::Util::ADSI::Group#add_member_sids')
+ sids = self.class.name_sid_hash(names)
+ add_member_sids(*sids.values)
end
alias add_member add_members
def remove_members(*names)
- names.each do |name|
- native_group.Remove(Puppet::Util::ADSI::User.uri(name, Puppet::Util::ADSI.computer_name))
- end
+ Puppet.deprecation_warning('Puppet::Util::ADSI::Group#remove_members is deprecated; please use Puppet::Util::ADSI::Group#remove_member_sids')
+ sids = self.class.name_sid_hash(names)
+ remove_member_sids(*sids.values)
end
alias remove_member remove_members
+ def add_member_sids(*sids)
+ sids.each do |sid|
+ native_group.Add(Puppet::Util::ADSI.sid_uri(sid))
+ end
+ end
+
+ def remove_member_sids(*sids)
+ sids.each do |sid|
+ native_group.Remove(Puppet::Util::ADSI.sid_uri(sid))
+ end
+ end
+
def members
# WIN32OLE objects aren't enumerable, so no map
members = []
@@ -254,18 +301,27 @@ module Puppet::Util::ADSI
members
end
+ def member_sids
+ sids = []
+ native_group.Members.each do |m|
+ sids << Puppet::Util::Windows::Security.octet_string_to_sid_object(m.objectSID)
+ end
+ sids
+ end
+
def set_members(desired_members)
return if desired_members.nil? or desired_members.empty?
- current_members = self.members
+ current_hash = Hash[ self.member_sids.map { |sid| [sid.to_s, sid] } ]
+ desired_hash = self.class.name_sid_hash(desired_members)
# First we add all missing members
- members_to_add = desired_members - current_members
- add_members(*members_to_add)
+ members_to_add = (desired_hash.keys - current_hash.keys).map { |sid| desired_hash[sid] }
+ add_member_sids(*members_to_add)
# Then we remove all extra members
- members_to_remove = current_members - desired_members
- remove_members(*members_to_remove)
+ members_to_remove = (current_hash.keys - desired_hash.keys).map { |sid| current_hash[sid] }
+ remove_member_sids(*members_to_remove)
end
def self.create(name)
@@ -283,7 +339,7 @@ module Puppet::Util::ADSI
end
def self.each(&block)
- wql = Puppet::Util::ADSI.execquery( "select name from win32_group" )
+ wql = Puppet::Util::ADSI.execquery( 'select name from win32_group where localaccount = "TRUE"' )
groups = []
wql.each do |g|
diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb
index fc407a971..82e0560e8 100644
--- a/lib/puppet/util/autoload.rb
+++ b/lib/puppet/util/autoload.rb
@@ -85,7 +85,7 @@ class Puppet::Util::Autoload
# returns nil if no file is found
def get_file(name, env=nil)
name = name + '.rb' unless name =~ /\.rb$/
- path = search_directories(env).find { |dir| File.exist?(File.join(dir, name)) }
+ path = search_directories(env).find { |dir| Puppet::FileSystem::File.exist?(File.join(dir, name)) }
path and File.join(path, name)
end
@@ -112,7 +112,7 @@ class Puppet::Util::Autoload
# We're using a per-thread cache of module directories so that we don't
# scan the filesystem each time we try to load something. This is reset
# at the beginning of compilation and at the end of an agent run.
- Thread.current[:env_module_directories] ||= {}
+ $env_module_directories ||= {}
# This is a little bit of a hack. Basically, the autoloader is being
@@ -136,7 +136,7 @@ class Puppet::Util::Autoload
# --cprice 2012-03-16
if Puppet.settings.app_defaults_initialized?
# if the app defaults have been initialized then it should be safe to access the module path setting.
- Thread.current[:env_module_directories][real_env] ||= real_env.modulepath.collect do |dir|
+ $env_module_directories[real_env] ||= real_env.modulepath.collect do |dir|
Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f) }
end.flatten.collect { |d| File.join(d, "lib") }.find_all do |d|
FileTest.directory?(d)
diff --git a/lib/puppet/util/backups.rb b/lib/puppet/util/backups.rb
index b1daf78fa..8ae14c190 100644
--- a/lib/puppet/util/backups.rb
+++ b/lib/puppet/util/backups.rb
@@ -10,7 +10,7 @@ module Puppet::Util::Backups
# let the path be specified
file ||= self[:path]
- return true unless FileTest.exists?(file)
+ return true unless Puppet::FileSystem::File.exist?(file)
return(self.bucket ? perform_backup_with_bucket(file) : perform_backup_with_backuplocal(file, self[:backup]))
end
@@ -19,7 +19,7 @@ module Puppet::Util::Backups
def perform_backup_with_bucket(fileobj)
file = (fileobj.class == String) ? fileobj : fileobj.name
- case File.lstat(file).ftype
+ case Puppet::FileSystem::File.new(file).lstat.ftype
when "directory"
# we don't need to backup directories when recurse is on
return true if self[:recurse]
@@ -58,7 +58,7 @@ module Puppet::Util::Backups
end
begin
- stat = File.send(method, newfile)
+ stat = Puppet::FileSystem::File.new(newfile).send(method)
rescue Errno::ENOENT
return
end
@@ -70,7 +70,7 @@ module Puppet::Util::Backups
info "Removing old backup of type #{stat.ftype}"
begin
- File.unlink(newfile)
+ Puppet::FileSystem::File.unlink(newfile)
rescue => detail
message = "Could not remove old backup: #{detail}"
self.log_exception(detail, message)
diff --git a/lib/puppet/util/cacher.rb b/lib/puppet/util/cacher.rb
index 136c9973e..24017de32 100644
--- a/lib/puppet/util/cacher.rb
+++ b/lib/puppet/util/cacher.rb
@@ -1,5 +1,3 @@
-require 'monitor'
-
module Puppet::Util::Cacher
# Our module has been extended in a class; we can only add the Instance methods,
# which become *class* methods in the class.
@@ -33,10 +31,8 @@ module Puppet::Util::Cacher
define_method(name.to_s + "=") do |value|
# Make sure the cache timestamp is set
- value_cache.synchronize do
- value_cache[name] = value
- set_expiration(name)
- end
+ value_cache[name] = value
+ set_expiration(name)
end
end
@@ -55,13 +51,11 @@ module Puppet::Util::Cacher
private
def cached_value(name)
- value_cache.synchronize do
- if value_cache[name].nil? or expired_by_ttl?(name)
- value_cache[name] = send("init_#{name}")
- set_expiration(name)
- end
- value_cache[name]
+ if value_cache[name].nil? or expired_by_ttl?(name)
+ value_cache[name] = send("init_#{name}")
+ set_expiration(name)
end
+ value_cache[name]
end
def expired_by_ttl?(name)
@@ -74,7 +68,7 @@ module Puppet::Util::Cacher
end
def value_cache
- @value_cache ||= {}.extend(MonitorMixin)
+ @value_cache ||= {}
end
end
end
diff --git a/lib/puppet/util/checksums.rb b/lib/puppet/util/checksums.rb
index a505bfc72..1f28fe8a7 100644
--- a/lib/puppet/util/checksums.rb
+++ b/lib/puppet/util/checksums.rb
@@ -56,7 +56,7 @@ module Puppet::Util::Checksums
# Return the :mtime timestamp of a file.
def mtime_file(filename)
- File.stat(filename).send(:mtime)
+ Puppet::FileSystem::File.new(filename).stat.send(:mtime)
end
# by definition this doesn't exist
@@ -102,7 +102,7 @@ module Puppet::Util::Checksums
# Return the :ctime of a file.
def ctime_file(filename)
- File.stat(filename).send(:ctime)
+ Puppet::FileSystem::File.new(filename).stat.send(:ctime)
end
alias :ctime_stream :mtime_stream
diff --git a/lib/puppet/util/classgen.rb b/lib/puppet/util/classgen.rb
index 8785f87b3..ee1ea5f46 100644
--- a/lib/puppet/util/classgen.rb
+++ b/lib/puppet/util/classgen.rb
@@ -1,3 +1,5 @@
+require 'puppet/util/methodhelper'
+
module Puppet
class ConstantAlreadyDefined < Error; end
class SubclassAlreadyDefined < Error; end
@@ -67,7 +69,7 @@ module Puppet::Util::ClassGen
options = symbolize_options(options)
const = genconst_string(name, options)
retval = false
- if const_defined?(const)
+ if is_constant_defined?(const)
remove_const(const)
retval = true
end
diff --git a/lib/puppet/util/colors.rb b/lib/puppet/util/colors.rb
index 37bcddf4a..a10e240a1 100644
--- a/lib/puppet/util/colors.rb
+++ b/lib/puppet/util/colors.rb
@@ -82,6 +82,7 @@ module Puppet::Util::Colors
if Puppet::Util::Platform.windows?
# We're on windows, need win32console for color to work
begin
+ require 'Win32API'
require 'win32console'
require 'windows/wide_string'
diff --git a/lib/puppet/util/command_line.rb b/lib/puppet/util/command_line.rb
index 1612691e8..a334ef8db 100644
--- a/lib/puppet/util/command_line.rb
+++ b/lib/puppet/util/command_line.rb
@@ -13,6 +13,7 @@ require 'puppet'
require 'puppet/util'
require "puppet/util/plugins"
require "puppet/util/rubygems"
+require "puppet/util/limits"
module Puppet
module Util
@@ -20,6 +21,8 @@ module Puppet
# is basically where the bootstrapping process / lifecycle of an app
# begins.
class CommandLine
+ include Puppet::Util::Limits
+
OPTION_OR_MANIFEST_FILE = /^-|\.pp$|\.rb$/
# @param zero [String] the name of the executable
@@ -83,6 +86,8 @@ module Puppet
Puppet.initialize_settings(args)
end
+ setpriority(Puppet[:priority])
+
find_subcommand.run
end
diff --git a/lib/puppet/util/docs.rb b/lib/puppet/util/docs.rb
index 0e9277f97..0c854b76c 100644
--- a/lib/puppet/util/docs.rb
+++ b/lib/puppet/util/docs.rb
@@ -20,10 +20,10 @@ module Puppet::Util::Docs
def doc
extra = methods.find_all { |m| m.to_s =~ /^dochook_.+/ }.sort.collect { |m|
self.send(m)
- }.delete_if {|r| r.nil? }.join(" ")
+ }.delete_if {|r| r.nil? }.collect {|r| "* #{r}"}.join("\n")
if @doc
- @doc + (extra.empty? ? '' : "\n\n" + extra)
+ scrub(@doc) + (extra.empty? ? '' : "\n\n#{extra}")
else
extra
end
@@ -63,6 +63,7 @@ module Puppet::Util::Docs
str + "\n"
end
+ # There is nothing that would ever set this. It gets read in reference/type.rb, but will never have any value but nil.
attr_reader :nodoc
def nodoc?
nodoc
@@ -89,33 +90,38 @@ module Puppet::Util::Docs
str << "\n"
end
- # Handle the inline indentation in the docs.
+ # Strip indentation and trailing whitespace from embedded doc fragments.
+ #
+ # Multi-line doc fragments are sometimes indented in order to preserve the
+ # formatting of the code they're embedded in. Since indents are syntactic
+ # elements in Markdown, we need to make sure we remove any indent that was
+ # added solely to preserve surrounding code formatting, but LEAVE any indent
+ # that delineates a Markdown element (code blocks, multi-line bulleted list
+ # items). We can do this by removing the *least common indent* from each line.
+ #
+ # Least common indent is defined as follows:
+ #
+ # * Find the smallest amount of leading space on any line...
+ # * ...excluding the first line (which may have zero indent without affecting
+ # the common indent)...
+ # * ...and excluding lines that consist solely of whitespace.
+ # * The least common indent may be a zero-length string, if the fragment is
+ # not indented to match code.
+ # * If there are hard tabs for some dumb reason, we assume they're at least
+ # consistent within this doc fragment.
+ #
+ # See tests in spec/unit/util/docs_spec.rb for examples.
def scrub(text)
- # Stupid markdown
- #text = text.gsub("<%=", "&lt;%=")
- # For text with no carriage returns, there's nothing to do.
- return text if text !~ /\n/
- indent = nil
-
- # If we can match an indentation, then just remove that same level of
- # indent from every line. However, ignore any indentation on the
- # first line, since that can be inconsistent.
- text = text.lstrip
- text.gsub!(/^([\t]+)/) { |s| " "*8*s.length; } # Expand leading tabs
- # Find first non-empty line after the first line:
- line2start = (text =~ /(\n?\s*\n)/)
- line2start += $1.length
- if (text[line2start..-1] =~ /^([ ]+)\S/) == 0
- indent = Regexp.quote($1)
- begin
- return text.gsub(/^#{indent}/,'')
- rescue => detail
- Puppet.log_exception(detail)
- end
- else
- return text
+ # One-liners are easy!
+ return text.strip if text !~ /\n/
+ excluding_first_line = text.partition("\n").last
+ indent = excluding_first_line.scan(/^[ \t]*(?=\S)/).min || '' # prevent nil
+ # Clean hanging indent, if any
+ if indent.length > 0
+ text = text.gsub(/^#{indent}/, '')
end
-
+ # Clean trailing space
+ text.lines.map{|line|line.rstrip}.join("\n").rstrip
end
module_function :scrub
diff --git a/lib/puppet/util/execution.rb b/lib/puppet/util/execution.rb
index 5f0089303..e931816f6 100644
--- a/lib/puppet/util/execution.rb
+++ b/lib/puppet/util/execution.rb
@@ -1,15 +1,33 @@
module Puppet
require 'rbconfig'
- # A command failed to execute.
require 'puppet/error'
+ # A command failed to execute.
+ # @api public
class ExecutionFailure < Puppet::Error
end
+end
# This module defines methods for execution of system commands. It is intented for inclusion
# in classes that needs to execute system commands.
# @api public
-module Util::Execution
+module Puppet::Util::Execution
+
+ # This is the full output from a process. The object itself (a String) is the
+ # stdout of the process.
+ #
+ # @api public
+ class ProcessOutput < String
+ # @return [Integer] The exit status of the process
+ # @api public
+ attr_reader :exitstatus
+
+ # @api private
+ def initialize(value,exitstatus)
+ super(value)
+ @exitstatus = exitstatus
+ end
+ end
# Executes the provided command with STDIN connected to a pipe, yielding the
# pipe object.
@@ -27,17 +45,12 @@ module Util::Execution
# @yield [pipe] to a block executing a subprocess
# @yieldparam pipe [IO] the opened pipe
# @yieldreturn [String] the output to return
- # @raise [ExecutionFailure] if the executed chiled process did not exit with status == 0 and `failonfail` is
+ # @raise [Puppet::ExecutionFailure] if the executed chiled process did not exit with status == 0 and `failonfail` is
# `true`.
# @return [String] a string with the output from the subprocess executed by the given block
+ # @api public
#
def self.execpipe(command, failonfail = true)
- if respond_to? :debug
- debug "Executing '#{command}'"
- else
- Puppet.debug "Executing '#{command}'"
- end
-
# Paste together an array with spaces. We used to paste directly
# together, no spaces, which made for odd invocations; the user had to
# include whitespace between arguments.
@@ -46,13 +59,20 @@ module Util::Execution
# shell anyhow, while no spaces makes for a small developer cost every
# time this is invoked. --daniel 2012-02-13
command_str = command.respond_to?(:join) ? command.join(' ') : command
+
+ if respond_to? :debug
+ debug "Executing '#{command_str}'"
+ else
+ Puppet.debug "Executing '#{command_str}'"
+ end
+
output = open("| #{command_str} 2>&1") do |pipe|
yield pipe
end
if failonfail
unless $CHILD_STATUS == 0
- raise ExecutionFailure, output
+ raise Puppet::ExecutionFailure, output
end
end
@@ -62,10 +82,11 @@ module Util::Execution
# Wraps execution of {execute} with mapping of exception to given exception (and output as argument).
# @raise [exception] under same conditions as {execute}, but raises the given `exception` with the output as argument
# @return (see execute)
+ # @api public
def self.execfail(command, exception)
output = execute(command)
return output
- rescue ExecutionFailure
+ rescue Puppet::ExecutionFailure
raise exception, output
end
@@ -80,8 +101,8 @@ module Util::Execution
# @param options [Hash] a Hash of options
# @option options [Boolean] :failonfail if this value is set to true, then this method will raise an error if the
# command is not executed successfully.
- # @option options [?] :uid (nil) the user id of the user that the process should be run as
- # @option options [?] :gid (nil) the group id of the group that the process should be run as
+ # @option options [Integer, String] :uid (nil) the user id of the user that the process should be run as
+ # @option options [Integer, String] :gid (nil) the group id of the group that the process should be run as
# @option options [Boolean] :combine sets whether or not to combine stdout/stderr in the output
# @option options [String] :stdinfile (nil) sets a file that can be used for stdin. Passing a string for stdin is not currently
# supported.
@@ -92,13 +113,16 @@ module Util::Execution
# Passing in a value of false for this option will allow the command to be executed using the user/system locale.
# @option options [Hash<{String => String}>] :custom_environment ({}) a hash of key/value pairs to set as environment variables for the duration
# of the command.
- # @return [String] output as specified by options
+ # @return [Puppet::Util::Execution::ProcessOutput] output as specified by options
+ # @raise [Puppet::ExecutionFailure] if the executed chiled process did not exit with status == 0 and `failonfail` is
+ # `true`.
# @note Unfortunately, the default behavior for failonfail and combine (since
# 0.22.4 and 0.24.7, respectively) depend on whether options are specified
# or not. If specified, then failonfail and combine default to false (even
# when the options specified are neither failonfail nor combine). If no
# options are specified, then failonfail and combine default to true.
# @comment See commits efe9a833c and d32d7f30
+ # @api public
#
def self.execute(command, options = NoOptionsSpecified)
# specifying these here rather than in the method signature to allow callers to pass in a partial
@@ -147,8 +171,8 @@ module Util::Execution
begin
exit_status = Puppet::Util::Windows::Process.wait_process(process_info.process_handle)
ensure
- Process.CloseHandle(process_info.process_handle)
- Process.CloseHandle(process_info.thread_handle)
+ Puppet::Util::Windows::Process.CloseHandle(process_info.process_handle)
+ Puppet::Util::Windows::Process.CloseHandle(process_info.thread_handle)
end
end
@@ -161,15 +185,16 @@ module Util::Execution
end
if options[:failonfail] and exit_status != 0
- raise ExecutionFailure, "Execution of '#{str}' returned #{exit_status}: #{output}"
+ raise Puppet::ExecutionFailure, "Execution of '#{str}' returned #{exit_status}: #{output}"
end
- output
+ Puppet::Util::Execution::ProcessOutput.new(output || '', exit_status)
end
# Returns the path to the ruby executable (available via Config object, even if
# it's not in the PATH... so this is slightly safer than just using Puppet::Util.which)
# @return [String] the path to the Ruby executable
+ # @api private
#
def self.ruby_path()
File.join(RbConfig::CONFIG['bindir'],
@@ -261,7 +286,7 @@ module Util::Execution
# about a race condition because all of the places that we call this from are preceded by a call to "waitpid2",
# meaning that the processes responsible for writing the file have completed before we get here.)
2.times do |try|
- if File.exists?(stdout.path)
+ if Puppet::FileSystem::File.exist?(stdout.path)
stdout.open
begin
return stdout.read
@@ -279,4 +304,3 @@ module Util::Execution
end
private_class_method :wait_for_output
end
-end
diff --git a/lib/puppet/util/filetype.rb b/lib/puppet/util/filetype.rb
index c65f8026e..7f43f8c10 100755..100644
--- a/lib/puppet/util/filetype.rb
+++ b/lib/puppet/util/filetype.rb
@@ -98,12 +98,12 @@ class Puppet::Util::FileType
newfiletype(:flat) do
# Back the file up before replacing it.
def backup
- bucket.backup(@path) if File.exists?(@path)
+ bucket.backup(@path) if Puppet::FileSystem::File.exist?(@path)
end
# Read the file.
def read
- if File.exist?(@path)
+ if Puppet::FileSystem::File.exist?(@path)
File.read(@path)
else
return nil
@@ -112,7 +112,7 @@ class Puppet::Util::FileType
# Remove the file.
def remove
- File.unlink(@path) if File.exist?(@path)
+ Puppet::FileSystem::File.unlink(@path) if Puppet::FileSystem::File.exist?(@path)
end
# Overwrite the file.
diff --git a/lib/puppet/util/instance_loader.rb b/lib/puppet/util/instance_loader.rb
index 7c2e18486..c6d6b71d4 100755..100644
--- a/lib/puppet/util/instance_loader.rb
+++ b/lib/puppet/util/instance_loader.rb
@@ -42,9 +42,9 @@ module Puppet::Util::InstanceLoader
# Use this method so they all get loaded
loaded_instances(type).sort { |a,b| a.to_s <=> b.to_s }.each do |name|
mod = self.loaded_instance(name)
- docs += "#{name}\n#{"-" * name.to_s.length}\n"
+ docs << "#{name}\n#{"-" * name.to_s.length}\n"
- docs += Puppet::Util::Docs.scrub(mod.doc) + "\n\n"
+ docs << Puppet::Util::Docs.scrub(mod.doc) << "\n\n"
end
docs
diff --git a/lib/puppet/util/instrumentation.rb b/lib/puppet/util/instrumentation.rb
index eb522437e..94a75ac03 100644
--- a/lib/puppet/util/instrumentation.rb
+++ b/lib/puppet/util/instrumentation.rb
@@ -5,7 +5,6 @@ require 'puppet/util/instance_loader'
class Puppet::Util::Instrumentation
extend Puppet::Util::ClassGen
extend Puppet::Util::InstanceLoader
- extend MonitorMixin
# we're using a ruby lazy autoloader to prevent a loop when requiring listeners
# since this class sets up an indirection which is also used in Puppet::Indirector::Indirection
@@ -71,11 +70,9 @@ class Puppet::Util::Instrumentation
end
def self.each_listener(label)
- synchronize {
- @listeners_of[label] ||= @listeners.select do |k,l|
- l.listen_to?(label)
- end
- }.each do |l|
+ @listeners_of[label] ||= @listeners.select do |k,l|
+ l.listen_to?(label)
+ end.each do |l|
yield l
end
end
@@ -105,67 +102,51 @@ class Puppet::Util::Instrumentation
end
def self.subscribe(listener, label_pattern, event)
- synchronize {
- raise "Listener #{listener.name} is already subscribed" if @listeners.include?(listener.name)
- Puppet.debug "registering instrumentation listener #{listener.name}"
- @listeners[listener.name] = Listener.new(listener, label_pattern, event)
- listener.subscribed if listener.respond_to?(:subscribed)
- rehash
- }
+ raise "Listener #{listener.name} is already subscribed" if @listeners.include?(listener.name)
+ Puppet.debug "registering instrumentation listener #{listener.name}"
+ @listeners[listener.name] = Listener.new(listener, label_pattern, event)
+ listener.subscribed if listener.respond_to?(:subscribed)
+ rehash
end
def self.unsubscribe(listener)
- synchronize {
- Puppet.warning("#{listener.name} hasn't been registered but asked to be unregistered") unless @listeners.include?(listener.name)
- Puppet.info "unregistering instrumentation listener #{listener.name}"
- @listeners.delete(listener.name)
- listener.unsubscribed if listener.respond_to?(:unsubscribed)
- rehash
- }
+ Puppet.warning("#{listener.name} hasn't been registered but asked to be unregistered") unless @listeners.include?(listener.name)
+ Puppet.info "unregistering instrumentation listener #{listener.name}"
+ @listeners.delete(listener.name)
+ listener.unsubscribed if listener.respond_to?(:unsubscribed)
+ rehash
end
def self.init
# let's init our probe indirection
require 'puppet/util/instrumentation/indirection_probe'
- synchronize {
- @listeners ||= {}
- @listeners_of ||= {}
- instance_loader(:listener).loadall
- }
+ @listeners ||= {}
+ @listeners_of ||= {}
+ instance_loader(:listener).loadall
end
def self.clear
- synchronize {
- @listeners = {}
- @listeners_of = {}
- @id = 0
- }
+ @listeners = {}
+ @listeners_of = {}
+ @id = 0
end
def self.[](key)
- synchronize {
- @listeners[key.intern]
- }
+ @listeners[key.intern]
end
def self.[]=(key, value)
- synchronize {
- @listeners[key.intern] = value
- rehash
- }
+ @listeners[key.intern] = value
+ rehash
end
private
- # should be called only under the guard
- # self.synchronize
def self.rehash
@listeners_of = {}
end
def self.next_id
- synchronize {
- @id = (@id || 0) + 1
- }
+ @id = (@id || 0) + 1
end
end
diff --git a/lib/puppet/util/instrumentation/data.rb b/lib/puppet/util/instrumentation/data.rb
index 9157f58fc..48e595432 100644
--- a/lib/puppet/util/instrumentation/data.rb
+++ b/lib/puppet/util/instrumentation/data.rb
@@ -20,12 +20,19 @@ class Puppet::Util::Instrumentation::Data
@listener.name
end
- def to_pson(*args)
- result = {
+ def to_data_hash
+ { :name => name }.merge(@listener.respond_to?(:data) ? @listener.data : {})
+ end
+
+ def to_pson_data_hash
+ {
'document_type' => "Puppet::Util::Instrumentation::Data",
- 'data' => { :name => name }.merge(@listener.respond_to?(:data) ? @listener.data : {})
+ 'data' => to_data_hash,
}
- result.to_pson(*args)
+ end
+
+ def to_pson(*args)
+ to_pson_data_hash.to_pson(*args)
end
def self.from_pson(data)
diff --git a/lib/puppet/util/instrumentation/indirection_probe.rb b/lib/puppet/util/instrumentation/indirection_probe.rb
index ad9323a38..237d8dbc8 100644
--- a/lib/puppet/util/instrumentation/indirection_probe.rb
+++ b/lib/puppet/util/instrumentation/indirection_probe.rb
@@ -15,12 +15,19 @@ class Puppet::Util::Instrumentation::IndirectionProbe
@probe_name = probe_name
end
- def to_pson(*args)
- result = {
+ def to_data_hash
+ { :name => probe_name }
+ end
+
+ def to_pson_data_hash
+ {
:document_type => "Puppet::Util::Instrumentation::IndirectionProbe",
- :data => { :name => probe_name }
+ :data => to_data_hash,
}
- result.to_pson(*args)
+ end
+
+ def to_pson(*args)
+ to_pson_data_hash.to_pson(*args)
end
def self.from_pson(data)
diff --git a/lib/puppet/util/instrumentation/instrumentable.rb b/lib/puppet/util/instrumentation/instrumentable.rb
index 4c6d2f97e..01c75ff77 100644
--- a/lib/puppet/util/instrumentation/instrumentable.rb
+++ b/lib/puppet/util/instrumentation/instrumentable.rb
@@ -1,4 +1,3 @@
-require 'monitor'
require 'puppet/util/instrumentation'
# This is the central point of all declared probes.
@@ -15,7 +14,7 @@ require 'puppet/util/instrumentation'
# end
# end
module Puppet::Util::Instrumentation::Instrumentable
- INSTRUMENTED_CLASSES = {}.extend(MonitorMixin)
+ INSTRUMENTED_CLASSES = {}
attr_reader :probes
@@ -101,10 +100,8 @@ module Puppet::Util::Instrumentation::Instrumentable
# end
#
def probe(method, options = {})
- INSTRUMENTED_CLASSES.synchronize {
- (@probes ||= []) << Probe.new(method, self, options)
- INSTRUMENTED_CLASSES[self] = @probes
- }
+ (@probes ||= []) << Probe.new(method, self, options)
+ INSTRUMENTED_CLASSES[self] = @probes
end
def self.probes
@@ -126,18 +123,14 @@ module Puppet::Util::Instrumentation::Instrumentable
end
def self.clear_probes
- INSTRUMENTED_CLASSES.synchronize {
- INSTRUMENTED_CLASSES.clear
- }
+ INSTRUMENTED_CLASSES.clear
nil # do not leak our probes to the exterior world
end
def self.each_probe
- INSTRUMENTED_CLASSES.synchronize {
- INSTRUMENTED_CLASSES.each_key do |klass|
- klass.probes.each { |probe| yield probe }
- end
- }
+ INSTRUMENTED_CLASSES.each_key do |klass|
+ klass.probes.each { |probe| yield probe }
+ end
nil # do not leak our probes to the exterior world
end
end
diff --git a/lib/puppet/util/instrumentation/listener.rb b/lib/puppet/util/instrumentation/listener.rb
index 42ec0c0e9..b965e976f 100644
--- a/lib/puppet/util/instrumentation/listener.rb
+++ b/lib/puppet/util/instrumentation/listener.rb
@@ -41,16 +41,23 @@ class Puppet::Util::Instrumentation::Listener
{ :data => @listener.data }
end
- def to_pson(*args)
- result = {
+ def to_data_hash
+ {
+ :name => name,
+ :pattern => pattern,
+ :enabled => enabled?
+ }
+ end
+
+ def to_pson_data_hash
+ {
:document_type => "Puppet::Util::Instrumentation::Listener",
- :data => {
- :name => name,
- :pattern => pattern,
- :enabled => enabled?
- }
+ :data => to_data_hash,
}
- result.to_pson(*args)
+ end
+
+ def to_pson(*args)
+ to_pson_data_hash.to_pson(*args)
end
def self.from_pson(data)
diff --git a/lib/puppet/util/instrumentation/listeners/log.rb b/lib/puppet/util/instrumentation/listeners/log.rb
index fcff14621..72797d628 100644
--- a/lib/puppet/util/instrumentation/listeners/log.rb
+++ b/lib/puppet/util/instrumentation/listeners/log.rb
@@ -1,5 +1,3 @@
-require 'monitor'
-
# This is an example instrumentation listener that stores the last
# 20 instrumented probe run time.
Puppet::Util::Instrumentation.new_listener(:log) do
@@ -9,21 +7,17 @@ Puppet::Util::Instrumentation.new_listener(:log) do
attr_accessor :last_logs
def initialize
- @last_logs = {}.extend(MonitorMixin)
+ @last_logs = {}
end
def notify(label, event, data)
return if event == :start
log_line = "#{label} took #{data[:finished] - data[:started]}"
- @last_logs.synchronize {
- (@last_logs[label] ||= []) << log_line
- @last_logs[label].shift if @last_logs[label].length > SIZE
- }
+ (@last_logs[label] ||= []) << log_line
+ @last_logs[label].shift if @last_logs[label].length > SIZE
end
def data
- @last_logs.synchronize {
- @last_logs.dup
- }
+ @last_logs.dup
end
end
diff --git a/lib/puppet/util/instrumentation/listeners/performance.rb b/lib/puppet/util/instrumentation/listeners/performance.rb
index 3ad51b7de..5bf3e2c5b 100644
--- a/lib/puppet/util/instrumentation/listeners/performance.rb
+++ b/lib/puppet/util/instrumentation/listeners/performance.rb
@@ -1,30 +1,24 @@
-require 'monitor'
-
Puppet::Util::Instrumentation.new_listener(:performance) do
attr_reader :samples
def initialize
- @samples = {}.extend(MonitorMixin)
+ @samples = {}
end
def notify(label, event, data)
return if event == :start
duration = data[:finished] - data[:started]
- samples.synchronize do
- @samples[label] ||= { :count => 0, :max => 0, :min => nil, :sum => 0, :average => 0 }
- @samples[label][:count] += 1
- @samples[label][:sum] += duration
- @samples[label][:max] = [ @samples[label][:max], duration ].max
- @samples[label][:min] = [ @samples[label][:min], duration ].reject { |val| val.nil? }.min
- @samples[label][:average] = @samples[label][:sum] / @samples[label][:count]
- end
+ @samples[label] ||= { :count => 0, :max => 0, :min => nil, :sum => 0, :average => 0 }
+ @samples[label][:count] += 1
+ @samples[label][:sum] += duration
+ @samples[label][:max] = [ @samples[label][:max], duration ].max
+ @samples[label][:min] = [ @samples[label][:min], duration ].reject { |val| val.nil? }.min
+ @samples[label][:average] = @samples[label][:sum] / @samples[label][:count]
end
def data
- samples.synchronize do
- @samples.dup
- end
+ @samples.dup
end
end
diff --git a/lib/puppet/util/limits.rb b/lib/puppet/util/limits.rb
new file mode 100644
index 000000000..e2f805b00
--- /dev/null
+++ b/lib/puppet/util/limits.rb
@@ -0,0 +1,12 @@
+require 'puppet/util'
+
+module Puppet::Util::Limits
+ # @api private
+ def setpriority(priority)
+ return unless priority
+
+ Process.setpriority(0, Process.pid, priority)
+ rescue Errno::EACCES, NotImplementedError
+ Puppet.warning("Failed to set process priority to '#{priority}'")
+ end
+end
diff --git a/lib/puppet/util/lockfile.rb b/lib/puppet/util/lockfile.rb
index 75bc97213..4f1ad5716 100644
--- a/lib/puppet/util/lockfile.rb
+++ b/lib/puppet/util/lockfile.rb
@@ -31,7 +31,7 @@ class Puppet::Util::Lockfile
def unlock
if locked?
- File.unlink(@file_path)
+ Puppet::FileSystem::File.unlink(@file_path)
true
else
false
@@ -56,7 +56,7 @@ class Puppet::Util::Lockfile
# being overridden by child classes.
# @return [boolean] true if the file is locked, false if it is not.
def file_locked?()
- File.exists? @file_path
+ Puppet::FileSystem::File.exist? @file_path
end
private :file_locked?
end
diff --git a/lib/puppet/util/log.rb b/lib/puppet/util/log.rb
index 3d34bba2c..808e0631d 100644
--- a/lib/puppet/util/log.rb
+++ b/lib/puppet/util/log.rb
@@ -1,5 +1,6 @@
require 'puppet/util/tagging'
require 'puppet/util/classgen'
+require 'puppet/network/format_support'
# Pass feedback to the user. Log levels are modeled after syslog's, and it is
# expected that that will be the most common log destination. Supports
@@ -8,6 +9,7 @@ class Puppet::Util::Log
include Puppet::Util
extend Puppet::Util::ClassGen
include Puppet::Util::Tagging
+ include Puppet::Network::FormatSupport
@levels = [:debug,:info,:notice,:warning,:err,:alert,:emerg,:crit]
@loglevel = 2
@@ -165,9 +167,7 @@ class Puppet::Util::Log
queuemessage(msg) if @destinations.length == 0
@destinations.each do |name, dest|
- threadlock(dest) do
- dest.handle(msg)
- end
+ dest.handle(msg)
end
end
@@ -265,7 +265,7 @@ class Puppet::Util::Log
@level = data['level'].intern
@message = data['message']
@source = data['source']
- @tags = data['tags']
+ @tags = Puppet::Util::TagSet.new(data['tags'])
@time = data['time']
if @time.is_a? String
@time = Time.parse(@time)
@@ -274,7 +274,11 @@ class Puppet::Util::Log
@line = data['line'] if data['line']
end
- def to_pson
+ def to_hash
+ self.to_data_hash
+ end
+
+ def to_data_hash
{
'level' => @level,
'message' => @message,
@@ -283,7 +287,11 @@ class Puppet::Util::Log
'time' => @time.iso8601(9),
'file' => @file,
'line' => @line,
- }.to_pson
+ }
+ end
+
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
def message=(msg)
diff --git a/lib/puppet/util/log/destinations.rb b/lib/puppet/util/log/destinations.rb
index e33cfd9a8..ecb19c66b 100644
--- a/lib/puppet/util/log/destinations.rb
+++ b/lib/puppet/util/log/destinations.rb
@@ -68,7 +68,7 @@ Puppet::Util::Log.newdesttype :file do
# first make sure the directory exists
# We can't just use 'Config.use' here, because they've
# specified a "special" destination.
- unless FileTest.exist?(File.dirname(path))
+ unless Puppet::FileSystem::File.exist?(File.dirname(path))
FileUtils.mkdir_p(File.dirname(path), :mode => 0755)
Puppet.info "Creating log directory #{File.dirname(path)}"
end
@@ -95,6 +95,28 @@ Puppet::Util::Log.newdesttype :file do
end
end
+Puppet::Util::Log.newdesttype :logstash_event do
+ require 'time'
+
+ def format(msg)
+ # logstash_event format is documented at
+ # https://logstash.jira.com/browse/LOGSTASH-675
+
+ data = {}
+ data = msg.to_hash
+ data['version'] = 1
+ data['@timestamp'] = data['time']
+ data.delete('time')
+
+ data
+ end
+
+ def handle(msg)
+ message = format(msg)
+ $stdout.puts message.to_pson
+ end
+end
+
Puppet::Util::Log.newdesttype :console do
require 'puppet/util/colors'
include Puppet::Util::Colors
diff --git a/lib/puppet/util/metric.rb b/lib/puppet/util/metric.rb
index 49d4edfc3..f37bbcfea 100644
--- a/lib/puppet/util/metric.rb
+++ b/lib/puppet/util/metric.rb
@@ -1,8 +1,10 @@
# included so we can test object types
require 'puppet'
+require 'puppet/network/format_support'
# A class for handling metrics. This is currently ridiculously hackish.
class Puppet::Util::Metric
+ include Puppet::Network::FormatSupport
attr_accessor :type, :name, :value, :label
attr_writer :values
@@ -15,12 +17,16 @@ class Puppet::Util::Metric
metric
end
- def to_pson
+ def to_data_hash
{
'name' => @name,
'label' => @label,
'values' => @values
- }.to_pson
+ }
+ end
+
+ def to_pson(*args)
+ to_data_hash.to_pson(*args)
end
# Return a specific value
@@ -155,7 +161,7 @@ class Puppet::Util::Metric
Puppet.warning "RRD library is missing; cannot store metrics"
return
end
- self.create(time - 5) unless FileTest.exists?(self.path)
+ self.create(time - 5) unless Puppet::FileSystem::File.exist?(self.path)
if Puppet.features.rrd_legacy? && ! Puppet.features.rrd?
@rrd ||= RRDtool.new(self.path)
diff --git a/lib/puppet/util/monkey_patches.rb b/lib/puppet/util/monkey_patches.rb
index b593ae3d3..3f0e68e8a 100644
--- a/lib/puppet/util/monkey_patches.rb
+++ b/lib/puppet/util/monkey_patches.rb
@@ -105,6 +105,7 @@ class IO
end
def self.binread(name, length = nil, offset = 0)
+ Puppet.deprecation_warning("This is a monkey-patched implementation of IO.binread on ruby 1.8 and is deprecated. Read the file without this method as it will be removed in a future version.")
File.open(name, 'rb') do |f|
f.seek(offset) if offset > 0
f.read(length)
@@ -194,8 +195,12 @@ if Puppet::Util::Platform.windows?
def set_default_paths
# This can be removed once openssl integrates with windows
# cert store, see http://rt.openssl.org/Ticket/Display.html?id=2158
- Puppet::Util::Windows::RootCerts.instance.each do |x509|
- add_cert(x509)
+ Puppet::Util::Windows::RootCerts.instance.to_a.uniq.each do |x509|
+ begin
+ add_cert(x509)
+ rescue OpenSSL::X509::StoreError => e
+ warn "Failed to add #{x509.subject.to_s}"
+ end
end
__original_set_default_paths
diff --git a/lib/puppet/util/network_device/config.rb b/lib/puppet/util/network_device/config.rb
index ef49dd393..fe355708a 100644
--- a/lib/puppet/util/network_device/config.rb
+++ b/lib/puppet/util/network_device/config.rb
@@ -15,7 +15,7 @@ class Puppet::Util::NetworkDevice::Config
attr_reader :devices
def exists?
- FileTest.exists?(@file)
+ Puppet::FileSystem::File.exist?(@file)
end
def initialize
diff --git a/lib/puppet/util/plugins.rb b/lib/puppet/util/plugins.rb
index 0bea67d05..dde496d35 100644
--- a/lib/puppet/util/plugins.rb
+++ b/lib/puppet/util/plugins.rb
@@ -37,7 +37,7 @@ module Puppet
def self.known
Paths[Loaded.length...Paths.length].each { |path|
file = File.join(path,'plugin_init.rb')
- Loaded << (File.exist?(file) && new(file))
+ Loaded << (Puppet::FileSystem::File.exist?(file) && new(file))
}
Loaded.compact
end
diff --git a/lib/puppet/util/posix.rb b/lib/puppet/util/posix.rb
index 2af0e4b91..2af0e4b91 100755..100644
--- a/lib/puppet/util/posix.rb
+++ b/lib/puppet/util/posix.rb
diff --git a/lib/puppet/util/profiler.rb b/lib/puppet/util/profiler.rb
index 0c3a3768f..c8d1762f5 100644
--- a/lib/puppet/util/profiler.rb
+++ b/lib/puppet/util/profiler.rb
@@ -10,14 +10,19 @@ module Puppet::Util::Profiler
NONE = Puppet::Util::Profiler::None.new
+ # Reset the profiling system to the original state
+ def self.clear
+ @profiler = nil
+ end
+
# @return This thread's configured profiler
def self.current
- Thread.current[:profiler] || NONE
+ @profiler || NONE
end
# @param profiler [#profile] A profiler for the current thread
def self.current=(profiler)
- Thread.current[:profiler] = profiler
+ @profiler = profiler
end
# @param message [String] A description of the profiled event
diff --git a/lib/puppet/util/provider_features.rb b/lib/puppet/util/provider_features.rb
index d557c0380..f8c0c0aa6 100644
--- a/lib/puppet/util/provider_features.rb
+++ b/lib/puppet/util/provider_features.rb
@@ -84,7 +84,7 @@ module Puppet::Util::ProviderFeatures
names = @features.keys.sort { |a,b| a.to_s <=> b.to_s }
names.each do |name|
doc = @features[name].docs.gsub(/\n\s+/, " ")
- str += "- *#{name}*: #{doc}\n"
+ str << "- *#{name}*: #{doc}\n"
end
if providers.length > 0
@@ -101,7 +101,7 @@ module Puppet::Util::ProviderFeatures
end
end
end
- str += doctable(headers, data)
+ str << doctable(headers, data)
end
str
end
diff --git a/lib/puppet/util/rdoc.rb b/lib/puppet/util/rdoc.rb
index becfa4ba3..49784956b 100644
--- a/lib/puppet/util/rdoc.rb
+++ b/lib/puppet/util/rdoc.rb
@@ -5,44 +5,42 @@ module Puppet::Util::RDoc
# launch a rdoc documenation process
# with the files/dir passed in +files+
def rdoc(outputdir, files, charset = nil)
- unless Puppet.features.rdoc1?
- raise "the version of RDoc included in Ruby #{::RUBY_VERSION} is not supported"
- end
-
- begin
- Puppet[:ignoreimport] = true
+ Puppet[:ignoreimport] = true
- # then rdoc
- require 'rdoc/rdoc'
- require 'rdoc/options'
+ # then rdoc
+ require 'rdoc/rdoc'
+ require 'rdoc/options'
- # load our parser
- require 'puppet/util/rdoc/parser'
+ # load our parser
+ require 'puppet/util/rdoc/parser'
- r = RDoc::RDoc.new
+ r = RDoc::RDoc.new
+ if Puppet.features.rdoc1?
RDoc::RDoc::GENERATORS["puppet"] = RDoc::RDoc::Generator.new(
- "puppet/util/rdoc/generators/puppet_generator.rb",
- :PuppetGenerator,
- "puppet"
- )
-
- # specify our own format & where to output
- options = [ "--fmt", "puppet",
- "--quiet",
- "--exclude", "/modules/[^/]*/files/.*$",
- "--exclude", "/modules/[^/]*/templates/.*$",
- "--op", outputdir ]
+ "puppet/util/rdoc/generators/puppet_generator.rb",
+ :PuppetGenerator,
+ "puppet"
+ )
+ end
- options << "--force-update" if Options::OptionList.options.any? { |o| o[0] == "--force-update" }
- options += [ "--charset", charset] if charset
- options += files
+ # specify our own format & where to output
+ options = [ "--fmt", "puppet",
+ "--quiet",
+ "--exclude", "/modules/[^/]*/spec/.*$",
+ "--exclude", "/modules/[^/]*/files/.*$",
+ "--exclude", "/modules/[^/]*/tests/.*$",
+ "--exclude", "/modules/[^/]*/templates/.*$",
+ "--op", outputdir ]
- # launch the documentation process
- r.document(options)
- rescue RDoc::RDocError => e
- raise Puppet::ParseError.new("RDoc error #{e}")
+ if !Puppet.features.rdoc1? || ::Options::OptionList.options.any? { |o| o[0] == "--force-update" } # Options is a root object in the rdoc1 namespace...
+ options << "--force-update"
end
+ options += [ "--charset", charset] if charset
+ options += files
+
+ # launch the documentation process
+ r.document(options)
end
# launch an output to console manifest doc
diff --git a/lib/puppet/util/rdoc/code_objects.rb b/lib/puppet/util/rdoc/code_objects.rb
index 3c789a0c5..7edd4134e 100644
--- a/lib/puppet/util/rdoc/code_objects.rb
+++ b/lib/puppet/util/rdoc/code_objects.rb
@@ -9,25 +9,20 @@ module RDoc
# PuppetGenerator.
# PuppetTopLevel is a top level (usually a .pp/.rb file)
- class PuppetTopLevel < TopLevel
+ module PuppetTopLevel
attr_accessor :module_name, :global
+ end
- # will contain all plugins
- @@all_plugins = {}
-
- # contains all cutoms facts
- @@all_facts = {}
-
- def initialize(toplevel)
- super(toplevel.file_relative_name)
- end
-
- def self.all_plugins
- @@all_plugins.values
- end
-
- def self.all_facts
- @@all_facts.values
+ # Add top level comments to a class or module regardless of whether we are
+ # using rdoc1 or rdoc2+
+ # @api private
+ module AddClassModuleComment
+ def add_comment(comment, location = nil)
+ if PUPPET_RDOC_VERSION == 1
+ self.comment = comment
+ else
+ super
+ end
end
end
@@ -35,28 +30,67 @@ module RDoc
# This is mapped to an HTMLPuppetModule
# it leverage the RDoc (ruby) module infrastructure
class PuppetModule < NormalModule
+ include AddClassModuleComment
+
attr_accessor :facts, :plugins
def initialize(name,superclass=nil)
@facts = []
@plugins = []
+ @nodes = {}
super(name,superclass)
end
- def initialize_classes_and_modules
- super
- @nodes = {}
+ def add_plugin(plugin)
+ if PUPPET_RDOC_VERSION == 1
+ add_to(@plugins, plugin)
+ else
+ add_plugin_rdoc2(plugin)
+ end
end
- def add_plugin(plugin)
- add_to(@plugins, plugin)
+ def add_plugin_rdoc2(plugin)
+ name = plugin.name
+ type = plugin.type
+ meth = AnyMethod.new("*args", name)
+ meth.params = "(*args)"
+ meth.visibility = :public
+ meth.document_self = true
+ meth.singleton = false
+ meth.comment = plugin.comment
+ if type == 'function'
+ @function_container ||= add_module(NormalModule, "__functions__")
+ @function_container.add_method(meth)
+ elsif type == 'type'
+ @type_container ||= add_module(NormalModule, "__types__")
+ @type_container.add_method(meth)
+ end
end
def add_fact(fact)
- add_to(@facts, fact)
+ if PUPPET_RDOC_VERSION == 1
+ add_to(@facts, fact)
+ else
+ add_fact_rdoc2(fact)
+ end
+ end
+
+ def add_fact_rdoc2(fact)
+ @fact_container ||= add_module(NormalModule, "__facts__")
+ confine_str = fact.confine.empty? ? '' : fact.confine.to_s
+ const = Constant.new(fact.name, confine_str, fact.comment)
+ @fact_container.add_constant(const)
+ end
+
+ def add_node(name, superclass)
+ if PUPPET_RDOC_VERSION == 1
+ add_node_rdoc1(name, superclass)
+ else
+ add_node_rdoc2(name, superclass)
+ end
end
- def add_node(name,superclass)
+ def add_node_rdoc1(name, superclass)
cls = @nodes[name]
unless cls
cls = PuppetNode.new(name, superclass)
@@ -67,6 +101,18 @@ module RDoc
cls
end
+ # Adds a module called __nodes__ and adds nodes to it as classes
+ #
+ def add_node_rdoc2(name,superclass)
+ if cls = @nodes[name]
+ return cls
+ end
+ @node_container ||= add_module(NormalModule, "__nodes__")
+ cls = @node_container.add_class(PuppetNode, name, superclass)
+ @nodes[name] = cls if !@done_documenting
+ cls
+ end
+
def each_fact
@facts.each {|c| yield c}
end
@@ -88,6 +134,8 @@ module RDoc
# It is mapped to a HTMLPuppetClass for display
# It leverages RDoc (ruby) Class
class PuppetClass < ClassModule
+ include AddClassModuleComment
+
attr_accessor :resource_list, :requires, :childs, :realizes
def initialize(name, superclass)
@@ -130,7 +178,7 @@ module RDoc
# but are written class1::class2::define we need to perform the lookup by
# ourselves.
def find_symbol(symbol, method=nil)
- result = super
+ result = super(symbol)
if not result and symbol =~ /::/
modules = symbol.split(/::/)
unless modules.empty?
@@ -169,6 +217,8 @@ module RDoc
# It is mapped to a HTMLPuppetNode for display
# A node is just a variation of a class
class PuppetNode < PuppetClass
+ include AddClassModuleComment
+
def initialize(name, superclass)
super(name,superclass)
end
diff --git a/lib/puppet/util/rdoc/generators/puppet_generator.rb b/lib/puppet/util/rdoc/generators/puppet_generator.rb
index 5c6aca28e..142124769 100644
--- a/lib/puppet/util/rdoc/generators/puppet_generator.rb
+++ b/lib/puppet/util/rdoc/generators/puppet_generator.rb
@@ -246,7 +246,7 @@ module Generators
end
def gen_composite_index(collection, template, filename)\
- return if FileTest.exists?(filename)
+ return if Puppet::FileSystem::File.exist?(filename)
template = TemplatePage.new(RDoc::Page::FR_INDEX_BODY, template)
res1 = []
diff --git a/lib/puppet/util/rdoc/parser.rb b/lib/puppet/util/rdoc/parser.rb
index 6b5ad740b..6ecd4921c 100644
--- a/lib/puppet/util/rdoc/parser.rb
+++ b/lib/puppet/util/rdoc/parser.rb
@@ -6,492 +6,17 @@
# rdoc mandatory includes
require "rdoc/code_objects"
require "puppet/util/rdoc/code_objects"
-require "rdoc/tokenstream"
-if ::RUBY_VERSION =~ /^1.8/
- require "rdoc/markup/simple_markup/preprocess"
- require "rdoc/parsers/parserfactory"
-else
- require "rdoc/markup/preprocess"
- require "rdoc/parser"
-end
-
-module RDoc
-
-class Parser
- extend ParserFactory if ::RUBY_VERSION =~ /^1.8/
-
- SITE = "__site__"
-
- attr_accessor :input_file_name, :top_level
-
- # parser registration into RDoc
- parse_files_matching(/\.(rb|pp)$/)
-
- # called with the top level file
- def initialize(top_level, file_name, content, options, stats)
- @options = options
- @stats = stats
- @input_file_name = file_name
- @top_level = PuppetTopLevel.new(top_level)
- @progress = $stderr unless options.quiet
- end
-
- # main entry point
- def scan
- environment = Puppet::Node::Environment.new
- @known_resource_types = environment.known_resource_types
- unless environment.known_resource_types.watching_file?(@input_file_name)
- Puppet.info "rdoc: scanning #{@input_file_name}"
- if @input_file_name =~ /\.pp$/
- @parser = Puppet::Parser::Parser.new(environment)
- @parser.file = @input_file_name
- @parser.parse.instantiate('').each do |type|
- @known_resource_types.add type
- end
- end
- end
-
- scan_top_level(@top_level)
- @top_level
- end
-
- # Due to a bug in RDoc, we need to roll our own find_module_named
- # The issue is that RDoc tries harder by asking the parent for a class/module
- # of the name. But by doing so, it can mistakenly use a module of same name
- # but from which we are not descendant.
- def find_object_named(container, name)
- return container if container.name == name
- container.each_classmodule do |m|
- return m if m.name == name
- end
- nil
- end
-
- # walk down the namespace and lookup/create container as needed
- def get_class_or_module(container, name)
-
- # class ::A -> A is in the top level
- if name =~ /^::/
- container = @top_level
- end
-
- names = name.split('::')
-
- final_name = names.pop
- names.each do |name|
- prev_container = container
- container = find_object_named(container, name)
- container ||= prev_container.add_class(PuppetClass, name, nil)
- end
- [container, final_name]
- end
-
- # split_module tries to find if +path+ belongs to the module path
- # if it does, it returns the module name, otherwise if we are sure
- # it is part of the global manifest path, "__site__" is returned.
- # And finally if this path couldn't be mapped anywhere, nil is returned.
- def split_module(path)
- # find a module
- fullpath = File.expand_path(path)
- Puppet.debug "rdoc: testing #{fullpath}"
- if fullpath =~ /(.*)\/([^\/]+)\/(?:manifests|plugins|lib)\/.+\.(pp|rb)$/
- modpath = $1
- name = $2
- Puppet.debug "rdoc: module #{name} into #{modpath} ?"
- Puppet::Node::Environment.new.modulepath.each do |mp|
- if File.identical?(modpath,mp)
- Puppet.debug "rdoc: found module #{name}"
- return name
- end
- end
- end
- if fullpath =~ /\.(pp|rb)$/
- # there can be paths we don't want to scan under modules
- # imagine a ruby or manifest that would be distributed as part as a module
- # but we don't want those to be hosted under <site>
- Puppet::Node::Environment.new.modulepath.each do |mp|
- # check that fullpath is a descendant of mp
- dirname = fullpath
- previous = dirname
- while (dirname = File.dirname(previous)) != previous
- previous = dirname
- return nil if File.identical?(dirname,mp)
- end
- end
- end
- # we are under a global manifests
- Puppet.debug "rdoc: global manifests"
- SITE
- end
-
- # create documentation for the top level +container+
- def scan_top_level(container)
- # use the module README as documentation for the module
- comment = ""
- %w{README README.rdoc}.each do |rfile|
- readme = File.join(File.dirname(File.dirname(@input_file_name)), rfile)
- comment = File.open(readme,"r") { |f| f.read } if FileTest.readable?(readme)
- end
- look_for_directives_in(container, comment) unless comment.empty?
-
- # infer module name from directory
- name = split_module(@input_file_name)
- if name.nil?
- # skip .pp files that are not in manifests directories as we can't guarantee they're part
- # of a module or the global configuration.
- container.document_self = false
- return
- end
-
- Puppet.debug "rdoc: scanning for #{name}"
-
- container.module_name = name
- container.global=true if name == SITE
-
- @stats.num_modules += 1
- container, name = get_class_or_module(container,name)
- mod = container.add_module(PuppetModule, name)
- mod.record_location(@top_level)
- mod.comment = comment
-
- if @input_file_name =~ /\.pp$/
- parse_elements(mod)
- elsif @input_file_name =~ /\.rb$/
- parse_plugins(mod)
- end
- end
-
- # create documentation for include statements we can find in +code+
- # and associate it with +container+
- def scan_for_include_or_require(container, code)
- code = [code] unless code.is_a?(Array)
- code.each do |stmt|
- scan_for_include_or_require(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
-
- if stmt.is_a?(Puppet::Parser::AST::Function) and ['include','require'].include?(stmt.name)
- stmt.arguments.each do |included|
- Puppet.debug "found #{stmt.name}: #{included}"
- container.send("add_#{stmt.name}",Include.new(included.to_s, stmt.doc))
- end
- end
- end
- end
-
- # create documentation for realize statements we can find in +code+
- # and associate it with +container+
- def scan_for_realize(container, code)
- code = [code] unless code.is_a?(Array)
- code.each do |stmt|
- scan_for_realize(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
-
- if stmt.is_a?(Puppet::Parser::AST::Function) and stmt.name == 'realize'
- stmt.arguments.each do |realized|
- Puppet.debug "found #{stmt.name}: #{realized}"
- container.add_realize(Include.new(realized.to_s, stmt.doc))
- end
- end
- end
- end
-
- # create documentation for global variables assignements we can find in +code+
- # and associate it with +container+
- def scan_for_vardef(container, code)
- code = [code] unless code.is_a?(Array)
- code.each do |stmt|
- scan_for_vardef(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
-
- if stmt.is_a?(Puppet::Parser::AST::VarDef)
- Puppet.debug "rdoc: found constant: #{stmt.name} = #{stmt.value}"
- container.add_constant(Constant.new(stmt.name.to_s, stmt.value.to_s, stmt.doc))
- end
- end
- end
-
- # create documentation for resources we can find in +code+
- # and associate it with +container+
- def scan_for_resource(container, code)
- code = [code] unless code.is_a?(Array)
- code.each do |stmt|
- scan_for_resource(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::ASTArray)
-
- if stmt.is_a?(Puppet::Parser::AST::Resource) and !stmt.type.nil?
- begin
- type = stmt.type.split("::").collect { |s| s.capitalize }.join("::")
- stmt.instances.each do |inst|
- title = inst.title.is_a?(Puppet::Parser::AST::ASTArray) ? inst.title.to_s.gsub(/\[(.*)\]/,'\1') : inst.title.to_s
- Puppet.debug "rdoc: found resource: #{type}[#{title}]"
-
- param = []
- inst.parameters.children.each do |p|
- res = {}
- res["name"] = p.param
- res["value"] = "#{p.value.to_s}" unless p.value.nil?
-
- param << res
- end
-
- container.add_resource(PuppetResource.new(type, title, stmt.doc, param))
- end
- rescue => detail
- raise Puppet::ParseError, "impossible to parse resource in #{stmt.file} at line #{stmt.line}: #{detail}"
- end
- end
- end
- end
-
- def resource_stmt_to_ref(stmt)
- type = stmt.type.split("::").collect { |s| s.capitalize }.join("::")
- title = stmt.title.is_a?(Puppet::Parser::AST::ASTArray) ? stmt.title.to_s.gsub(/\[(.*)\]/,'\1') : stmt.title.to_s
-
- param = stmt.params.children.collect do |p|
- {"name" => p.param, "value" => p.value.to_s}
- end
- PuppetResource.new(type, title, stmt.doc, param)
- end
-
- # create documentation for a class named +name+
- def document_class(name, klass, container)
- Puppet.debug "rdoc: found new class #{name}"
- container, name = get_class_or_module(container, name)
-
- superclass = klass.parent
- superclass = "" if superclass.nil? or superclass.empty?
-
- @stats.num_classes += 1
- comment = klass.doc
- look_for_directives_in(container, comment) unless comment.empty?
- cls = container.add_class(PuppetClass, name, superclass)
- # it is possible we already encountered this class, while parsing some namespaces
- # from other classes of other files. But at that time we couldn't know this class superclass
- # so, now we know it and force it.
- cls.superclass = superclass
- cls.record_location(@top_level)
-
- # scan class code for include
- code = klass.code.children if klass.code.is_a?(Puppet::Parser::AST::ASTArray)
- code ||= klass.code
- unless code.nil?
- scan_for_include_or_require(cls, code)
- scan_for_realize(cls, code)
- scan_for_resource(cls, code) if Puppet.settings[:document_all]
- end
-
- cls.comment = comment
- rescue => detail
- raise Puppet::ParseError, "impossible to parse class '#{name}' in #{klass.file} at line #{klass.line}: #{detail}"
- end
-
- # create documentation for a node
- def document_node(name, node, container)
- Puppet.debug "rdoc: found new node #{name}"
- superclass = node.parent
- superclass = "" if superclass.nil? or superclass.empty?
-
- comment = node.doc
- look_for_directives_in(container, comment) unless comment.empty?
- n = container.add_node(name, superclass)
- n.record_location(@top_level)
-
- code = node.code.children if node.code.is_a?(Puppet::Parser::AST::ASTArray)
- code ||= node.code
- unless code.nil?
- scan_for_include_or_require(n, code)
- scan_for_realize(n, code)
- scan_for_vardef(n, code)
- scan_for_resource(n, code) if Puppet.settings[:document_all]
- end
-
- n.comment = comment
- rescue => detail
- raise Puppet::ParseError, "impossible to parse node '#{name}' in #{node.file} at line #{node.line}: #{detail}"
- end
-
- # create documentation for a define
- def document_define(name, define, container)
- Puppet.debug "rdoc: found new definition #{name}"
- # find superclas if any
- @stats.num_methods += 1
-
- # find the parent
- # split define name by :: to find the complete module hierarchy
- container, name = get_class_or_module(container,name)
-
- # build up declaration
- declaration = ""
- define.arguments.each do |arg,value|
- declaration << "\$#{arg}"
- unless value.nil?
- declaration << " => "
- case value
- when Puppet::Parser::AST::Leaf
- declaration << "'#{value.value}'"
- when Puppet::Parser::AST::ASTArray
- declaration << "[#{value.children.collect { |v| "'#{v}'" }.join(", ")}]"
- else
- declaration << "#{value.to_s}"
- end
- end
- declaration << ", "
- end
- declaration.chop!.chop! if declaration.size > 1
-
- # register method into the container
- meth = AnyMethod.new(declaration, name)
- meth.comment = define.doc
- container.add_method(meth)
- look_for_directives_in(container, meth.comment) unless meth.comment.empty?
- meth.params = "( #{declaration} )"
- meth.visibility = :public
- meth.document_self = true
- meth.singleton = false
- rescue => detail
- raise Puppet::ParseError, "impossible to parse definition '#{name}' in #{define.file} at line #{define.line}: #{detail}"
- end
-
- # Traverse the AST tree and produce code-objects node
- # that contains the documentation
- def parse_elements(container)
- Puppet.debug "rdoc: scanning manifest"
-
- @known_resource_types.hostclasses.values.sort { |a,b| a.name <=> b.name }.each do |klass|
- name = klass.name
- if klass.file == @input_file_name
- unless name.empty?
- document_class(name,klass,container)
- else # on main class document vardefs
- code = klass.code.children if klass.code.is_a?(Puppet::Parser::AST::ASTArray)
- code ||= klass.code
- scan_for_vardef(container, code) unless code.nil?
- end
- end
- end
-
- @known_resource_types.definitions.each do |name, define|
- if define.file == @input_file_name
- document_define(name,define,container)
- end
- end
-
- @known_resource_types.nodes.each do |name, node|
- if node.file == @input_file_name
- document_node(name.to_s,node,container)
- end
- end
- end
-
- # create documentation for plugins
- def parse_plugins(container)
- Puppet.debug "rdoc: scanning plugin or fact"
- if @input_file_name =~ /\/facter\/[^\/]+\.rb$/
- parse_fact(container)
- else
- parse_puppet_plugin(container)
- end
- end
-
- # this is a poor man custom fact parser :-)
- def parse_fact(container)
- comments = ""
- current_fact = nil
- File.open(@input_file_name) do |of|
- of.each do |line|
- # fetch comments
- if line =~ /^[ \t]*# ?(.*)$/
- comments += $1 + "\n"
- elsif line =~ /^[ \t]*Facter.add\(['"](.*?)['"]\)/
- current_fact = Fact.new($1,{})
- look_for_directives_in(container, comments) unless comments.empty?
- current_fact.comment = comments
- container.add_fact(current_fact)
- current_fact.record_location(@top_level)
- comments = ""
- Puppet.debug "rdoc: found custom fact #{current_fact.name}"
- elsif line =~ /^[ \t]*confine[ \t]*:(.*?)[ \t]*=>[ \t]*(.*)$/
- current_fact.confine = { :type => $1, :value => $2 } unless current_fact.nil?
- else # unknown line type
- comments =""
- end
- end
- end
- end
-
- # this is a poor man puppet plugin parser :-)
- # it doesn't extract doc nor desc :-(
- def parse_puppet_plugin(container)
- comments = ""
- current_plugin = nil
-
- File.open(@input_file_name) do |of|
- of.each do |line|
- # fetch comments
- if line =~ /^[ \t]*# ?(.*)$/
- comments += $1 + "\n"
- elsif line =~ /^[ \t]*newfunction[ \t]*\([ \t]*:(.*?)[ \t]*,[ \t]*:type[ \t]*=>[ \t]*(:rvalue|:lvalue)\)/
- current_plugin = Plugin.new($1, "function")
- container.add_plugin(current_plugin)
- look_for_directives_in(container, comments) unless comments.empty?
- current_plugin.comment = comments
- current_plugin.record_location(@top_level)
- comments = ""
- Puppet.debug "rdoc: found new function plugins #{current_plugin.name}"
- elsif line =~ /^[ \t]*Puppet::Type.newtype[ \t]*\([ \t]*:(.*?)\)/
- current_plugin = Plugin.new($1, "type")
- container.add_plugin(current_plugin)
- look_for_directives_in(container, comments) unless comments.empty?
- current_plugin.comment = comments
- current_plugin.record_location(@top_level)
- comments = ""
- Puppet.debug "rdoc: found new type plugins #{current_plugin.name}"
- elsif line =~ /module Puppet::Parser::Functions/
- # skip
- else # unknown line type
- comments =""
- end
- end
- end
- end
-
- # look_for_directives_in scans the current +comment+ for RDoc directives
- def look_for_directives_in(context, comment)
- preprocess = SM::PreProcess.new(@input_file_name, @options.rdoc_include)
-
- preprocess.handle(comment) do |directive, param|
- case directive
- when "stopdoc"
- context.stop_doc
- ""
- when "startdoc"
- context.start_doc
- context.force_documentation = true
- ""
- when "enddoc"
- #context.done_documenting = true
- #""
- throw :enddoc
- when "main"
- options = Options.instance
- options.main_page = param
- ""
- when "title"
- options = Options.instance
- options.title = param
- ""
- when "section"
- context.set_current_section(param, comment)
- comment.replace("") # 1.8 doesn't support #clear
- break
- else
- warn "Unrecognized directive '#{directive}'"
- break
- end
- end
- remove_private_comments(comment)
- end
-
- def remove_private_comments(comment)
- comment.gsub!(/^#--.*?^#\+\+/m, '')
- comment.sub!(/^#--.*/m, '')
- end
-end
+begin
+ # Rdoc 1 imports
+ require "rdoc/tokenstream"
+ require "rdoc/markup/simple_markup/preprocess"
+ require "rdoc/parsers/parserfactory"
+ require "puppet/util/rdoc/parser/puppet_parser_rdoc1.rb"
+rescue LoadError
+ # Current version imports
+ require "rdoc/token_stream"
+ require "rdoc/markup/pre_process"
+ require "rdoc/parser"
+ require "puppet/util/rdoc/parser/puppet_parser_rdoc2.rb"
end
diff --git a/lib/puppet/util/rdoc/parser/puppet_parser_core.rb b/lib/puppet/util/rdoc/parser/puppet_parser_core.rb
new file mode 100644
index 000000000..dd7d03caf
--- /dev/null
+++ b/lib/puppet/util/rdoc/parser/puppet_parser_core.rb
@@ -0,0 +1,477 @@
+# Functionality common to both our RDoc version 1 and 2 parsers.
+module RDoc::PuppetParserCore
+
+ SITE = "__site__"
+
+ def self.included(base)
+ base.class_eval do
+ attr_accessor :input_file_name, :top_level
+
+ # parser registration into RDoc
+ parse_files_matching(/\.(rb|pp)$/)
+ end
+ end
+
+ # called with the top level file
+ def initialize(top_level, file_name, body, options, stats)
+ @options = options
+ @stats = stats
+ @input_file_name = file_name
+ @top_level = top_level
+ @top_level.extend(RDoc::PuppetTopLevel)
+ @progress = $stderr unless options.quiet
+ end
+
+ # main entry point
+ def scan
+ environment = Puppet::Node::Environment.new
+ @known_resource_types = environment.known_resource_types
+ unless environment.known_resource_types.watching_file?(@input_file_name)
+ Puppet.info "rdoc: scanning #{@input_file_name}"
+ if @input_file_name =~ /\.pp$/
+ @parser = Puppet::Parser::Parser.new(environment)
+ @parser.file = @input_file_name
+ @parser.parse.instantiate('').each do |type|
+ @known_resource_types.add type
+ end
+ end
+ end
+
+ scan_top_level(@top_level)
+ @top_level
+ end
+
+ # Due to a bug in RDoc, we need to roll our own find_module_named
+ # The issue is that RDoc tries harder by asking the parent for a class/module
+ # of the name. But by doing so, it can mistakenly use a module of same name
+ # but from which we are not descendant.
+ def find_object_named(container, name)
+ return container if container.name == name
+ container.each_classmodule do |m|
+ return m if m.name == name
+ end
+ nil
+ end
+
+ # walk down the namespace and lookup/create container as needed
+ def get_class_or_module(container, name)
+
+ # class ::A -> A is in the top level
+ if name =~ /^::/
+ container = @top_level
+ end
+
+ names = name.split('::')
+
+ final_name = names.pop
+ names.each do |name|
+ prev_container = container
+ container = find_object_named(container, name)
+ container ||= prev_container.add_class(RDoc::PuppetClass, name, nil)
+ end
+ [container, final_name]
+ end
+
+ # split_module tries to find if +path+ belongs to the module path
+ # if it does, it returns the module name, otherwise if we are sure
+ # it is part of the global manifest path, "__site__" is returned.
+ # And finally if this path couldn't be mapped anywhere, nil is returned.
+ def split_module(path)
+ # find a module
+ fullpath = File.expand_path(path)
+ Puppet.debug "rdoc: testing #{fullpath}"
+ if fullpath =~ /(.*)\/([^\/]+)\/(?:manifests|plugins|lib)\/.+\.(pp|rb)$/
+ modpath = $1
+ name = $2
+ Puppet.debug "rdoc: module #{name} into #{modpath} ?"
+ Puppet::Node::Environment.new.modulepath.each do |mp|
+ if File.identical?(modpath,mp)
+ Puppet.debug "rdoc: found module #{name}"
+ return name
+ end
+ end
+ end
+ if fullpath =~ /\.(pp|rb)$/
+ # there can be paths we don't want to scan under modules
+ # imagine a ruby or manifest that would be distributed as part as a module
+ # but we don't want those to be hosted under <site>
+ Puppet::Node::Environment.new.modulepath.each do |mp|
+ # check that fullpath is a descendant of mp
+ dirname = fullpath
+ previous = dirname
+ while (dirname = File.dirname(previous)) != previous
+ previous = dirname
+ return nil if File.identical?(dirname,mp)
+ end
+ end
+ end
+ # we are under a global manifests
+ Puppet.debug "rdoc: global manifests"
+ SITE
+ end
+
+ # create documentation for the top level +container+
+ def scan_top_level(container)
+ # use the module README as documentation for the module
+ comment = ""
+ %w{README README.rdoc}.each do |rfile|
+ readme = File.join(File.dirname(File.dirname(@input_file_name)), rfile)
+ comment = File.open(readme,"r") { |f| f.read } if FileTest.readable?(readme)
+ end
+ look_for_directives_in(container, comment) unless comment.empty?
+
+ # infer module name from directory
+ name = split_module(@input_file_name)
+ if name.nil?
+ # skip .pp files that are not in manifests directories as we can't guarantee they're part
+ # of a module or the global configuration.
+ container.document_self = false
+ return
+ end
+
+ Puppet.debug "rdoc: scanning for #{name}"
+
+ container.module_name = name
+ container.global=true if name == SITE
+
+ container, name = get_class_or_module(container,name)
+ mod = container.add_module(RDoc::PuppetModule, name)
+ mod.record_location(@top_level)
+ mod.add_comment(comment, @input_file_name)
+
+ if @input_file_name =~ /\.pp$/
+ parse_elements(mod)
+ elsif @input_file_name =~ /\.rb$/
+ parse_plugins(mod)
+ end
+ end
+
+ # create documentation for include statements we can find in +code+
+ # and associate it with +container+
+ def scan_for_include_or_require(container, code)
+ code = [code] unless code.is_a?(Array)
+ code.each do |stmt|
+ scan_for_include_or_require(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::BlockExpression)
+
+ if stmt.is_a?(Puppet::Parser::AST::Function) and ['include','require'].include?(stmt.name)
+ stmt.arguments.each do |included|
+ Puppet.debug "found #{stmt.name}: #{included}"
+ container.send("add_#{stmt.name}", RDoc::Include.new(included.to_s, stmt.doc))
+ end
+ end
+ end
+ end
+
+ # create documentation for realize statements we can find in +code+
+ # and associate it with +container+
+ def scan_for_realize(container, code)
+ code = [code] unless code.is_a?(Array)
+ code.each do |stmt|
+ scan_for_realize(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::BlockExpression)
+
+ if stmt.is_a?(Puppet::Parser::AST::Function) and stmt.name == 'realize'
+ stmt.arguments.each do |realized|
+ Puppet.debug "found #{stmt.name}: #{realized}"
+ container.add_realize( RDoc::Include.new(realized.to_s, stmt.doc))
+ end
+ end
+ end
+ end
+
+ # create documentation for global variables assignements we can find in +code+
+ # and associate it with +container+
+ def scan_for_vardef(container, code)
+ code = [code] unless code.is_a?(Array)
+ code.each do |stmt|
+ scan_for_vardef(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::BlockExpression)
+
+ if stmt.is_a?(Puppet::Parser::AST::VarDef)
+ Puppet.debug "rdoc: found constant: #{stmt.name} = #{stmt.value}"
+ container.add_constant(RDoc::Constant.new(stmt.name.to_s, stmt.value.to_s, stmt.doc))
+ end
+ end
+ end
+
+ # create documentation for resources we can find in +code+
+ # and associate it with +container+
+ def scan_for_resource(container, code)
+ code = [code] unless code.is_a?(Array)
+ code.each do |stmt|
+ scan_for_resource(container,stmt.children) if stmt.is_a?(Puppet::Parser::AST::BlockExpression)
+
+ if stmt.is_a?(Puppet::Parser::AST::Resource) and !stmt.type.nil?
+ begin
+ type = stmt.type.split("::").collect { |s| s.capitalize }.join("::")
+ stmt.instances.each do |inst|
+ title = inst.title.is_a?(Puppet::Parser::AST::ASTArray) ? inst.title.to_s.gsub(/\[(.*)\]/,'\1') : inst.title.to_s
+ Puppet.debug "rdoc: found resource: #{type}[#{title}]"
+
+ param = []
+ inst.parameters.children.each do |p|
+ res = {}
+ res["name"] = p.param
+ res["value"] = "#{p.value.to_s}" unless p.value.nil?
+
+ param << res
+ end
+
+ container.add_resource(RDoc::PuppetResource.new(type, title, stmt.doc, param))
+ end
+ rescue => detail
+ raise Puppet::ParseError, "impossible to parse resource in #{stmt.file} at line #{stmt.line}: #{detail}"
+ end
+ end
+ end
+ end
+
+ # create documentation for a class named +name+
+ def document_class(name, klass, container)
+ Puppet.debug "rdoc: found new class #{name}"
+ container, name = get_class_or_module(container, name)
+
+ superclass = klass.parent
+ superclass = "" if superclass.nil? or superclass.empty?
+
+ comment = klass.doc
+ look_for_directives_in(container, comment) unless comment.empty?
+ cls = container.add_class(RDoc::PuppetClass, name, superclass)
+ # it is possible we already encountered this class, while parsing some namespaces
+ # from other classes of other files. But at that time we couldn't know this class superclass
+ # so, now we know it and force it.
+ cls.superclass = superclass
+ cls.record_location(@top_level)
+
+ # scan class code for include
+ code = klass.code.children if klass.code.is_a?(Puppet::Parser::AST::BlockExpression)
+ code ||= klass.code
+ unless code.nil?
+ scan_for_include_or_require(cls, code)
+ scan_for_realize(cls, code)
+ scan_for_resource(cls, code) if Puppet.settings[:document_all]
+ end
+
+ cls.add_comment(comment, klass.file)
+ rescue => detail
+ raise Puppet::ParseError, "impossible to parse class '#{name}' in #{klass.file} at line #{klass.line}: #{detail}"
+ end
+
+ # create documentation for a node
+ def document_node(name, node, container)
+ Puppet.debug "rdoc: found new node #{name}"
+ superclass = node.parent
+ superclass = "" if superclass.nil? or superclass.empty?
+
+ comment = node.doc
+ look_for_directives_in(container, comment) unless comment.empty?
+ n = container.add_node(name, superclass)
+ n.record_location(@top_level)
+
+ code = node.code.children if node.code.is_a?(Puppet::Parser::AST::BlockExpression)
+ code ||= node.code
+ unless code.nil?
+ scan_for_include_or_require(n, code)
+ scan_for_realize(n, code)
+ scan_for_vardef(n, code)
+ scan_for_resource(n, code) if Puppet.settings[:document_all]
+ end
+
+ n.add_comment(comment, node.file)
+ rescue => detail
+ raise Puppet::ParseError, "impossible to parse node '#{name}' in #{node.file} at line #{node.line}: #{detail}"
+ end
+
+ # create documentation for a define
+ def document_define(name, define, container)
+ Puppet.debug "rdoc: found new definition #{name}"
+ # find superclas if any
+
+ # find the parent
+ # split define name by :: to find the complete module hierarchy
+ container, name = get_class_or_module(container,name)
+
+ # build up declaration
+ declaration = ""
+ define.arguments.each do |arg,value|
+ declaration << "\$#{arg}"
+ unless value.nil?
+ declaration << " => "
+ case value
+ when Puppet::Parser::AST::Leaf
+ declaration << "'#{value.value}'"
+ when Puppet::Parser::AST::BlockExpression
+ declaration << "[#{value.children.collect { |v| "'#{v}'" }.join(", ")}]"
+ else
+ declaration << "#{value.to_s}"
+ end
+ end
+ declaration << ", "
+ end
+ declaration.chop!.chop! if declaration.size > 1
+
+ # register method into the container
+ meth = RDoc::AnyMethod.new(declaration, name)
+ meth.comment = define.doc
+ container.add_method(meth)
+ look_for_directives_in(container, meth.comment) unless meth.comment.empty?
+ meth.params = "( #{declaration} )"
+ meth.visibility = :public
+ meth.document_self = true
+ meth.singleton = false
+ rescue => detail
+ raise Puppet::ParseError, "impossible to parse definition '#{name}' in #{define.file} at line #{define.line}: #{detail}"
+ end
+
+ # Traverse the AST tree and produce code-objects node
+ # that contains the documentation
+ def parse_elements(container)
+ Puppet.debug "rdoc: scanning manifest"
+
+ @known_resource_types.hostclasses.values.sort { |a,b| a.name <=> b.name }.each do |klass|
+ name = klass.name
+ if klass.file == @input_file_name
+ unless name.empty?
+ document_class(name,klass,container)
+ else # on main class document vardefs
+ code = klass.code.children if klass.code.is_a?(Puppet::Parser::AST::BlockExpression)
+ code ||= klass.code
+ scan_for_vardef(container, code) unless code.nil?
+ end
+ end
+ end
+
+ @known_resource_types.definitions.each do |name, define|
+ if define.file == @input_file_name
+ document_define(name,define,container)
+ end
+ end
+
+ @known_resource_types.nodes.each do |name, node|
+ if node.file == @input_file_name
+ document_node(name.to_s,node,container)
+ end
+ end
+ end
+
+ # create documentation for plugins
+ def parse_plugins(container)
+ Puppet.debug "rdoc: scanning plugin or fact"
+ if @input_file_name =~ /\/facter\/[^\/]+\.rb$/
+ parse_fact(container)
+ else
+ parse_puppet_plugin(container)
+ end
+ end
+
+ # this is a poor man custom fact parser :-)
+ def parse_fact(container)
+ comments = ""
+ current_fact = nil
+ parsed_facts = []
+ File.open(@input_file_name) do |of|
+ of.each do |line|
+ # fetch comments
+ if line =~ /^[ \t]*# ?(.*)$/
+ comments += $1 + "\n"
+ elsif line =~ /^[ \t]*Facter.add\(['"](.*?)['"]\)/
+ current_fact = RDoc::Fact.new($1,{})
+ look_for_directives_in(container, comments) unless comments.empty?
+ current_fact.comment = comments
+ parsed_facts << current_fact
+ comments = ""
+ Puppet.debug "rdoc: found custom fact #{current_fact.name}"
+ elsif line =~ /^[ \t]*confine[ \t]*:(.*?)[ \t]*=>[ \t]*(.*)$/
+ current_fact.confine = { :type => $1, :value => $2 } unless current_fact.nil?
+ else # unknown line type
+ comments =""
+ end
+ end
+ end
+ parsed_facts.each do |f|
+ container.add_fact(f)
+ f.record_location(@top_level)
+ end
+ end
+
+ # this is a poor man puppet plugin parser :-)
+ # it doesn't extract doc nor desc :-(
+ def parse_puppet_plugin(container)
+ comments = ""
+ current_plugin = nil
+
+ File.open(@input_file_name) do |of|
+ of.each do |line|
+ # fetch comments
+ if line =~ /^[ \t]*# ?(.*)$/
+ comments += $1 + "\n"
+ elsif line =~ /^[ \t]*(?:Puppet::Parser::Functions::)?newfunction[ \t]*\([ \t]*:(.*?)[ \t]*,[ \t]*:type[ \t]*=>[ \t]*(:rvalue|:lvalue)/
+ current_plugin = RDoc::Plugin.new($1, "function")
+ look_for_directives_in(container, comments) unless comments.empty?
+ current_plugin.comment = comments
+ current_plugin.record_location(@top_level)
+ container.add_plugin(current_plugin)
+ comments = ""
+ Puppet.debug "rdoc: found new function plugins #{current_plugin.name}"
+ elsif line =~ /^[ \t]*Puppet::Type.newtype[ \t]*\([ \t]*:(.*?)\)/
+ current_plugin = RDoc::Plugin.new($1, "type")
+ look_for_directives_in(container, comments) unless comments.empty?
+ current_plugin.comment = comments
+ current_plugin.record_location(@top_level)
+ container.add_plugin(current_plugin)
+ comments = ""
+ Puppet.debug "rdoc: found new type plugins #{current_plugin.name}"
+ elsif line =~ /module Puppet::Parser::Functions/
+ # skip
+ else # unknown line type
+ comments =""
+ end
+ end
+ end
+ end
+
+ # New instance of the appropriate PreProcess for our RDoc version.
+ def create_rdoc_preprocess
+ raise(NotImplementedError, "This method must be overwritten for whichever version of RDoc this parser is working with")
+ end
+
+ # look_for_directives_in scans the current +comment+ for RDoc directives
+ def look_for_directives_in(context, comment)
+ preprocess = create_rdoc_preprocess
+
+ preprocess.handle(comment) do |directive, param|
+ case directive
+ when "stopdoc"
+ context.stop_doc
+ ""
+ when "startdoc"
+ context.start_doc
+ context.force_documentation = true
+ ""
+ when "enddoc"
+ #context.done_documenting = true
+ #""
+ throw :enddoc
+ when "main"
+ options = Options.instance
+ options.main_page = param
+ ""
+ when "title"
+ options = Options.instance
+ options.title = param
+ ""
+ when "section"
+ context.set_current_section(param, comment)
+ comment.replace("") # 1.8 doesn't support #clear
+ break
+ else
+ warn "Unrecognized directive '#{directive}'"
+ break
+ end
+ end
+ remove_private_comments(comment)
+ end
+
+ def remove_private_comments(comment)
+ comment.gsub!(/^#--.*?^#\+\+/m, '')
+ comment.sub!(/^#--.*/m, '')
+ end
+end
diff --git a/lib/puppet/util/rdoc/parser/puppet_parser_rdoc1.rb b/lib/puppet/util/rdoc/parser/puppet_parser_rdoc1.rb
new file mode 100644
index 000000000..656d2f7e7
--- /dev/null
+++ b/lib/puppet/util/rdoc/parser/puppet_parser_rdoc1.rb
@@ -0,0 +1,19 @@
+require 'puppet/util/rdoc/parser/puppet_parser_core.rb'
+
+module RDoc
+ PUPPET_RDOC_VERSION = 1
+
+ # @api private
+ class PuppetParserRDoc1
+ extend ParserFactory
+ include PuppetParserCore
+
+ def create_rdoc_preprocess
+ preprocess = SM::PreProcess.new(@input_file_name, @options.rdoc_include)
+ end
+ end
+
+ # For backwards compatibility
+ # @api private
+ Parser = PuppetParserRDoc1
+end
diff --git a/lib/puppet/util/rdoc/parser/puppet_parser_rdoc2.rb b/lib/puppet/util/rdoc/parser/puppet_parser_rdoc2.rb
new file mode 100644
index 000000000..75a5cdac3
--- /dev/null
+++ b/lib/puppet/util/rdoc/parser/puppet_parser_rdoc2.rb
@@ -0,0 +1,14 @@
+require 'puppet/util/rdoc/parser/puppet_parser_core.rb'
+
+module RDoc
+ PUPPET_RDOC_VERSION = 2
+
+ # @api private
+ class PuppetParserRDoc2 < Parser
+ include PuppetParserCore
+
+ def create_rdoc_preprocess
+ preprocess = Markup::PreProcess.new(@input_file_name, @options.rdoc_include)
+ end
+ end
+end
diff --git a/lib/puppet/util/reference.rb b/lib/puppet/util/reference.rb
index 2e2ee78f6..491e39b09 100644
--- a/lib/puppet/util/reference.rb
+++ b/lib/puppet/util/reference.rb
@@ -46,7 +46,7 @@ class Puppet::Util::Reference
# There used to be an attempt to use secure_open / replace_file to secure
# the target, too, but that did nothing: the race was still here. We can
# get exactly the same benefit from running this effort:
- File.unlink('/tmp/puppetdoc.tex') rescue nil
+ Puppet::FileSystem::File.unlink('/tmp/puppetdoc.tex') rescue nil
output = %x{#{cmd}}
unless $CHILD_STATUS == 0
$stderr.puts "rst2latex failed"
diff --git a/lib/puppet/util/resource_template.rb b/lib/puppet/util/resource_template.rb
index b12b125b5..bed585b21 100644
--- a/lib/puppet/util/resource_template.rb
+++ b/lib/puppet/util/resource_template.rb
@@ -44,7 +44,7 @@ class Puppet::Util::ResourceTemplate
end
def initialize(file, resource)
- raise ArgumentError, "Template #{file} does not exist" unless FileTest.exist?(file)
+ raise ArgumentError, "Template #{file} does not exist" unless Puppet::FileSystem::File.exist?(file)
@file = file
@resource = resource
end
diff --git a/lib/puppet/util/selinux.rb b/lib/puppet/util/selinux.rb
index 0b712be0d..78c3c4dfa 100644
--- a/lib/puppet/util/selinux.rb
+++ b/lib/puppet/util/selinux.rb
@@ -216,7 +216,7 @@ module Puppet::Util::SELinux
#
# @return [File::Stat] File.lstat result
def file_lstat(path)
- File.lstat(path)
+ Puppet::FileSystem::File.new(path).lstat
end
private :file_lstat
end
diff --git a/lib/puppet/util/storage.rb b/lib/puppet/util/storage.rb
index 32d744862..9df1cb501 100644
--- a/lib/puppet/util/storage.rb
+++ b/lib/puppet/util/storage.rb
@@ -45,7 +45,7 @@ class Puppet::Util::Storage
Puppet.settings.use(:main) unless FileTest.directory?(Puppet[:statedir])
filename = Puppet[:statefile]
- unless File.exists?(filename)
+ unless Puppet::FileSystem::File.exist?(filename)
self.init if @@state.nil?
return
end
@@ -80,7 +80,7 @@ class Puppet::Util::Storage
def self.store
Puppet.debug "Storing state"
- Puppet.info "Creating state file #{Puppet[:statefile]}" unless FileTest.exist?(Puppet[:statefile])
+ Puppet.info "Creating state file #{Puppet[:statefile]}" unless Puppet::FileSystem::File.exist?(Puppet[:statefile])
Puppet::Util.benchmark(:debug, "Stored state") do
Puppet::Util::Yaml.dump(@@state, Puppet[:statefile])
diff --git a/lib/puppet/util/suidmanager.rb b/lib/puppet/util/suidmanager.rb
index e2b063992..481e3864c 100644
--- a/lib/puppet/util/suidmanager.rb
+++ b/lib/puppet/util/suidmanager.rb
@@ -178,7 +178,7 @@ module Puppet::Util::SUIDManager
# :custom_environment (default {}) -- a hash of key/value pairs to set as environment variables for the duration
# of the command
def run_and_capture(command, new_uid=nil, new_gid=nil, options = {})
-
+ Puppet.deprecation_warning("Puppet::Util::SUIDManager.run_and_capture is deprecated; please use Puppet::Util::Execution.execute instead.")
# specifying these here rather than in the method signature to allow callers to pass in a partial
# set of overrides without affecting the default values for options that they don't pass in
default_options = {
diff --git a/lib/puppet/util/tag_set.rb b/lib/puppet/util/tag_set.rb
new file mode 100644
index 000000000..bd1029040
--- /dev/null
+++ b/lib/puppet/util/tag_set.rb
@@ -0,0 +1,29 @@
+require 'set'
+
+class Puppet::Util::TagSet < Set
+ def self.from_yaml(yaml)
+ self.new(YAML.load(yaml))
+ end
+
+ def to_yaml
+ @hash.keys.to_yaml
+ end
+
+ def self.from_pson(data)
+ self.new(data)
+ end
+
+ def to_pson(*args)
+ to_a.to_pson
+ end
+
+ # this makes puppet serialize it as an array for backwards
+ # compatibility
+ def to_zaml(z)
+ to_a.to_zaml(z)
+ end
+
+ def join(*args)
+ to_a.join(*args)
+ end
+end
diff --git a/lib/puppet/util/tagging.rb b/lib/puppet/util/tagging.rb
index 4161c0291..2e788279b 100644
--- a/lib/puppet/util/tagging.rb
+++ b/lib/puppet/util/tagging.rb
@@ -1,30 +1,10 @@
-# Created on 2008-01-19
-# Copyright Luke Kanies
+require 'puppet/util/tag_set'
-# A common module to handle tagging.
-#
-# So, do you want the bad news or the good news first?
-#
-# The bad news is that using an array here is hugely costly compared to using
-# a hash. Like, the same speed empty, 50 percent slower with one item, and
-# 300 percent slower at 6 - one of our common peaks for tagging items.
-#
-# ...and that assumes an efficient implementation, just using include?. These
-# methods have even more costs hidden in them.
-#
-# The good news is that this module has no API. Various objects directly
-# interact with their `@tags` member as an array, or dump it directly in YAML,
-# or whatever.
-#
-# So, er, you can't actually change this. No matter how much you want to be
-# cause it is inefficient in both CPU and object allocation terms.
-#
-# Good luck, my friend. --daniel 2012-07-17
module Puppet::Util::Tagging
# Add a tag to our current list. These tags will be added to all
# of the objects contained in this scope.
def tag(*ary)
- @tags ||= []
+ @tags ||= new_tags
qualified = []
@@ -45,12 +25,12 @@ module Puppet::Util::Tagging
# Return a copy of the tag list, so someone can't ask for our tags
# and then modify them.
def tags
- @tags ||= []
+ @tags ||= new_tags
@tags.dup
end
def tags=(tags)
- @tags = []
+ @tags = new_tags
return if tags.nil? or tags == ""
@@ -73,4 +53,8 @@ module Puppet::Util::Tagging
def valid_tag?(tag)
tag.is_a?(String) and tag =~ ValidTagRegex
end
+
+ def new_tags
+ Puppet::Util::TagSet.new
+ end
end
diff --git a/lib/puppet/util/watched_file.rb b/lib/puppet/util/watched_file.rb
index 6b28ab402..3e1195700 100755..100644
--- a/lib/puppet/util/watched_file.rb
+++ b/lib/puppet/util/watched_file.rb
@@ -26,7 +26,7 @@ class Puppet::Util::WatchedFile
end
# Allow this to be used as the name of the file being watched in various
- # other methods (such as File.exist?)
+ # other methods (such as Puppet::FileSystem::File.exist?)
def to_str
@filename
end
diff --git a/lib/puppet/util/watcher.rb b/lib/puppet/util/watcher.rb
index 78b21f8af..547c24c9e 100644
--- a/lib/puppet/util/watcher.rb
+++ b/lib/puppet/util/watcher.rb
@@ -7,7 +7,7 @@ module Puppet::Util::Watcher
def self.file_ctime_change_watcher(filename)
Puppet::Util::Watcher::ChangeWatcher.watch(lambda do
begin
- File.stat(filename).ctime
+ Puppet::FileSystem::File.new(filename).stat.ctime
rescue Errno::ENOENT, Errno::ENOTDIR
:absent
end
diff --git a/lib/puppet/util/windows.rb b/lib/puppet/util/windows.rb
index 612f72b78..af440552f 100644
--- a/lib/puppet/util/windows.rb
+++ b/lib/puppet/util/windows.rb
@@ -8,6 +8,9 @@ module Puppet::Util::Windows
require 'puppet/util/windows/process'
require 'puppet/util/windows/file'
require 'puppet/util/windows/root_certs'
+ require 'puppet/util/windows/access_control_entry'
+ require 'puppet/util/windows/access_control_list'
+ require 'puppet/util/windows/security_descriptor'
end
require 'puppet/util/windows/registry'
end
diff --git a/lib/puppet/util/windows/access_control_entry.rb b/lib/puppet/util/windows/access_control_entry.rb
new file mode 100644
index 000000000..5cd052dae
--- /dev/null
+++ b/lib/puppet/util/windows/access_control_entry.rb
@@ -0,0 +1,84 @@
+# Windows Access Control Entry
+#
+# Represents an access control entry, which grants or denies a subject,
+# identified by a SID, rights to a securable object.
+#
+# @see http://msdn.microsoft.com/en-us/library/windows/desktop/aa374868(v=vs.85).aspx
+# @api private
+class Puppet::Util::Windows::AccessControlEntry
+ require 'puppet/util/windows/security'
+ include Puppet::Util::Windows::SID
+
+ attr_accessor :sid
+ attr_reader :mask, :flags, :type
+
+ OBJECT_INHERIT_ACE = 0x1
+ CONTAINER_INHERIT_ACE = 0x2
+ NO_PROPAGATE_INHERIT_ACE = 0x4
+ INHERIT_ONLY_ACE = 0x8
+ INHERITED_ACE = 0x10
+
+ ACCESS_ALLOWED_ACE_TYPE = 0x0
+ ACCESS_DENIED_ACE_TYPE = 0x1
+
+ def initialize(sid, mask, flags = 0, type = ACCESS_ALLOWED_ACE_TYPE)
+ @sid = sid
+ @mask = mask
+ @flags = flags
+ @type = type
+ end
+
+ # Returns true if this ACE is inherited from a parent. If false,
+ # then the ACE is set directly on the object to which it refers.
+ #
+ # @return [Boolean] true if the ACE is inherited
+ def inherited?
+ (@flags & INHERITED_ACE) == INHERITED_ACE
+ end
+
+ # Returns true if this ACE only applies to children of the object.
+ # If false, it applies to the object.
+ #
+ # @return [Boolean] true if the ACE only applies to children and
+ # not the object itself.
+ def inherit_only?
+ (@flags & INHERIT_ONLY_ACE) == INHERIT_ONLY_ACE
+ end
+
+ # Returns true if this ACE applies to child directories.
+ #
+ # @return [Boolean] true if the ACE applies to child direcories
+ def container_inherit?
+ (@flags & CONTAINER_INHERIT_ACE) == CONTAINER_INHERIT_ACE
+ end
+
+ # Returns true if this ACE applies to child files.
+ #
+ # @return [Boolean] true if the ACE applies to child files.
+ def object_inherit?
+ (@flags & OBJECT_INHERIT_ACE) == OBJECT_INHERIT_ACE
+ end
+
+ def inspect
+ inheritance = ""
+ inheritance << '(I)' if inherited?
+ inheritance << '(OI)' if object_inherit?
+ inheritance << '(CI)' if container_inherit?
+ inheritance << '(IO)' if inherit_only?
+
+ left = "#{sid_to_name(sid)}:#{inheritance}"
+ left = left.ljust(45)
+ "#{left} 0x#{mask.to_s(16)}"
+ end
+
+ # Returns true if this ACE is equal to +other+
+ def ==(other)
+ self.class == other.class &&
+ sid == other.sid &&
+ mask == other.mask &&
+ flags == other.flags &&
+ type == other.type
+ end
+
+ alias eql? ==
+end
diff --git a/lib/puppet/util/windows/access_control_list.rb b/lib/puppet/util/windows/access_control_list.rb
new file mode 100644
index 000000000..14a924cd9
--- /dev/null
+++ b/lib/puppet/util/windows/access_control_list.rb
@@ -0,0 +1,106 @@
+# Windows Access Control List
+#
+# Represents a list of access control entries (ACEs).
+#
+# @see http://msdn.microsoft.com/en-us/library/windows/desktop/aa374872(v=vs.85).aspx
+# @api private
+class Puppet::Util::Windows::AccessControlList
+ include Enumerable
+
+ ACCESS_ALLOWED_ACE_TYPE = 0x0
+ ACCESS_DENIED_ACE_TYPE = 0x1
+
+ # Construct an ACL.
+ #
+ # @param acl [Enumerable] A list of aces to copy from.
+ def initialize(acl = nil)
+ if acl
+ @aces = acl.map(&:dup)
+ else
+ @aces = []
+ end
+ end
+
+ # Enumerate each ACE in the list.
+ #
+ # @yieldparam ace [Hash] the ace
+ def each
+ @aces.each {|ace| yield ace}
+ end
+
+ # Allow the +sid+ to access a resource with the specified access +mask+.
+ #
+ # @param sid [String] The SID that the ACE is granting access to
+ # @param mask [int] The access mask granted to the SID
+ # @param flags [int] The flags assigned to the ACE, e.g. +INHERIT_ONLY_ACE+
+ def allow(sid, mask, flags = 0)
+ @aces << Puppet::Util::Windows::AccessControlEntry.new(sid, mask, flags, ACCESS_ALLOWED_ACE_TYPE)
+ end
+
+ # Deny the +sid+ access to a resource with the specified access +mask+.
+ #
+ # @param sid [String] The SID that the ACE is denying access to
+ # @param mask [int] The access mask denied to the SID
+ # @param flags [int] The flags assigned to the ACE, e.g. +INHERIT_ONLY_ACE+
+ def deny(sid, mask, flags = 0)
+ @aces << Puppet::Util::Windows::AccessControlEntry.new(sid, mask, flags, ACCESS_DENIED_ACE_TYPE)
+ end
+
+ # Reassign all ACEs currently assigned to +old_sid+ to +new_sid+ instead.
+ # If an ACE is inherited or is not assigned to +old_sid+, then it will
+ # be copied as-is to the new ACL, preserving its order within the ACL.
+ #
+ # @param old_sid [String] The old SID, e.g. 'S-1-5-18'
+ # @param new_sid [String] The new SID
+ # @return [AccessControlList] The copied ACL.
+ def reassign!(old_sid, new_sid)
+ new_aces = []
+ prepend_needed = false
+ aces_to_prepend = []
+
+ @aces.each do |ace|
+ new_ace = ace.dup
+
+ if ace.sid == old_sid
+ if ace.inherited?
+ # create an explicit ACE granting or denying the
+ # new_sid the rights that the inherited ACE
+ # granted or denied the old_sid. We mask off all
+ # flags except those affecting inheritance of the
+ # ACE we're creating.
+ inherit_mask = Windows::Security::CONTAINER_INHERIT_ACE |
+ Windows::Security::OBJECT_INHERIT_ACE |
+ Windows::Security::INHERIT_ONLY_ACE
+ explicit_ace = Puppet::Util::Windows::AccessControlEntry.new(new_sid, ace.mask, ace.flags & inherit_mask, ace.type)
+ aces_to_prepend << explicit_ace
+ else
+ new_ace.sid = new_sid
+
+ prepend_needed = old_sid == Win32::Security::SID::LocalSystem
+ end
+ end
+ new_aces << new_ace
+ end
+
+ @aces = []
+
+ if prepend_needed
+ mask = Windows::Security::STANDARD_RIGHTS_ALL | Windows::Security::SPECIFIC_RIGHTS_ALL
+ ace = Puppet::Util::Windows::AccessControlEntry.new(
+ Win32::Security::SID::LocalSystem,
+ mask)
+ @aces << ace
+ end
+
+ @aces.concat(aces_to_prepend)
+ @aces.concat(new_aces)
+ end
+
+ def inspect
+ str = ""
+ @aces.each do |ace|
+ str << " #{ace.inspect}\n"
+ end
+ str
+ end
+end
diff --git a/lib/puppet/util/windows/file.rb b/lib/puppet/util/windows/file.rb
index d4b7bc1ca..141873071 100644
--- a/lib/puppet/util/windows/file.rb
+++ b/lib/puppet/util/windows/file.rb
@@ -1,6 +1,7 @@
require 'puppet/util/windows'
module Puppet::Util::Windows::File
+ require 'ffi'
require 'windows/api'
require 'windows/wide_string'
@@ -24,4 +25,216 @@ module Puppet::Util::Windows::File
new("MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})")
end
module_function :move_file_ex
+
+ module API
+ extend FFI::Library
+ ffi_lib 'kernel32'
+ ffi_convention :stdcall
+
+ # BOOLEAN WINAPI CreateSymbolicLink(
+ # _In_ LPTSTR lpSymlinkFileName, - symbolic link to be created
+ # _In_ LPTSTR lpTargetFileName, - name of target for symbolic link
+ # _In_ DWORD dwFlags - 0x0 target is a file, 0x1 target is a directory
+ # );
+ # rescue on Windows < 6.0 so that code doesn't explode
+ begin
+ attach_function :create_symbolic_link, :CreateSymbolicLinkW,
+ [:buffer_in, :buffer_in, :uint], :bool
+ rescue LoadError
+ end
+
+ # DWORD WINAPI GetFileAttributes(
+ # _In_ LPCTSTR lpFileName
+ # );
+ attach_function :get_file_attributes, :GetFileAttributesW,
+ [:buffer_in], :uint
+
+ # HANDLE WINAPI CreateFile(
+ # _In_ LPCTSTR lpFileName,
+ # _In_ DWORD dwDesiredAccess,
+ # _In_ DWORD dwShareMode,
+ # _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ # _In_ DWORD dwCreationDisposition,
+ # _In_ DWORD dwFlagsAndAttributes,
+ # _In_opt_ HANDLE hTemplateFile
+ # );
+ attach_function :create_file, :CreateFileW,
+ [:buffer_in, :uint, :uint, :pointer, :uint, :uint, :uint], :uint
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
+ # BOOL WINAPI DeviceIoControl(
+ # _In_ HANDLE hDevice,
+ # _In_ DWORD dwIoControlCode,
+ # _In_opt_ LPVOID lpInBuffer,
+ # _In_ DWORD nInBufferSize,
+ # _Out_opt_ LPVOID lpOutBuffer,
+ # _In_ DWORD nOutBufferSize,
+ # _Out_opt_ LPDWORD lpBytesReturned,
+ # _Inout_opt_ LPOVERLAPPED lpOverlapped
+ # );
+ attach_function :device_io_control, :DeviceIoControl,
+ [:uint, :uint, :pointer, :uint, :pointer, :uint, :pointer, :pointer], :bool
+
+ MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16384
+
+ # REPARSE_DATA_BUFFER
+ # http://msdn.microsoft.com/en-us/library/cc232006.aspx
+ # http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
+ # struct is always MAXIMUM_REPARSE_DATA_BUFFER_SIZE bytes
+ class ReparseDataBuffer < FFI::Struct
+ layout :reparse_tag, :uint,
+ :reparse_data_length, :ushort,
+ :reserved, :ushort,
+ :substitute_name_offset, :ushort,
+ :substitute_name_length, :ushort,
+ :print_name_offset, :ushort,
+ :print_name_length, :ushort,
+ :flags, :uint,
+ # max less above fields dword / uint 4 bytes, ushort 2 bytes
+ :path_buffer, [:uchar, MAXIMUM_REPARSE_DATA_BUFFER_SIZE - 20]
+ end
+
+ # BOOL WINAPI CloseHandle(
+ # _In_ HANDLE hObject
+ # );
+ attach_function :close_handle, :CloseHandle, [:uint], :bool
+ end
+
+ def symlink(target, symlink)
+ flags = File.directory?(target) ? 0x1 : 0x0
+ result = API.create_symbolic_link(WideString.new(symlink.to_s),
+ WideString.new(target.to_s), flags)
+ return true if result
+ raise Puppet::Util::Windows::Error.new(
+ "CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})")
+ end
+ module_function :symlink
+
+ INVALID_FILE_ATTRIBUTES = 0xFFFFFFFF #define INVALID_FILE_ATTRIBUTES (DWORD (-1))
+ def self.get_file_attributes(file_name)
+ result = API.get_file_attributes(WideString.new(file_name.to_s))
+ return result unless result == INVALID_FILE_ATTRIBUTES
+ raise Puppet::Util::Windows::Error.new("GetFileAttributes(#{file_name})")
+ end
+
+ INVALID_HANDLE_VALUE = -1 #define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
+ def self.create_file(file_name, desired_access, share_mode, security_attributes,
+ creation_disposition, flags_and_attributes, template_file_handle)
+
+ result = API.create_file(WideString.new(file_name.to_s),
+ desired_access, share_mode, security_attributes, creation_disposition,
+ flags_and_attributes, template_file_handle)
+
+ return result unless result == INVALID_HANDLE_VALUE
+ raise Puppet::Util::Windows::Error.new(
+ "CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, " +
+ "#{security_attributes}, #{creation_disposition.to_s(8)}, " +
+ "#{flags_and_attributes.to_s(8)}, #{template_file_handle})")
+ end
+
+ def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)
+ if out_buffer.nil?
+ raise Puppet::Util::Windows::Error.new("out_buffer is required")
+ end
+
+ result = API.device_io_control(
+ handle,
+ io_control_code,
+ in_buffer, in_buffer.nil? ? 0 : in_buffer.size,
+ out_buffer, out_buffer.size,
+ FFI::MemoryPointer.new(:uint, 1),
+ nil
+ )
+
+ return out_buffer if result
+ raise Puppet::Util::Windows::Error.new(
+ "DeviceIoControl(#{handle}, #{io_control_code}, #{in_buffer}, #{in_buffer.size}, " +
+ "#{out_buffer}, #{out_buffer.size}")
+ end
+
+ FILE_ATTRIBUTE_REPARSE_POINT = 0x400
+ def symlink?(file_name)
+ begin
+ attributes = get_file_attributes(file_name)
+ (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT
+ rescue
+ # raised INVALID_FILE_ATTRIBUTES is equivalent to file not found
+ false
+ end
+ end
+ module_function :symlink?
+
+ GENERIC_READ = 0x80000000
+ FILE_SHARE_READ = 1
+ OPEN_EXISTING = 3
+ FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
+ FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
+
+ def self.open_symlink(link_name)
+ begin
+ yield handle = create_file(
+ WideString.new(link_name.to_s),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ nil, # security_attributes
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ 0) # template_file
+ ensure
+ API.close_handle(handle) if handle
+ end
+ end
+
+ def readlink(link_name)
+ open_symlink(link_name) do |handle|
+ resolve_symlink(handle)
+ end
+ end
+ module_function :readlink
+
+ def stat(file_name)
+ file_name = file_name.to_s # accomodate PathName or String
+ stat = File.stat(file_name)
+ if symlink?(file_name)
+ link_ftype = File.stat(readlink(file_name)).ftype
+ # sigh, monkey patch instance method for instance, and close over link_ftype
+ singleton_class = class << stat; self; end
+ singleton_class.send(:define_method, :ftype) do
+ link_ftype
+ end
+ end
+ stat
+ end
+ module_function :stat
+
+ def lstat(file_name)
+ file_name = file_name.to_s # accomodate PathName or String
+ # monkey'ing around!
+ stat = File.lstat(file_name)
+ if symlink?(file_name)
+ def stat.ftype
+ "link"
+ end
+ end
+ stat
+ end
+ module_function :lstat
+
+ private
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa364571(v=vs.85).aspx
+ FSCTL_GET_REPARSE_POINT = 0x900a8
+
+ def self.resolve_symlink(handle)
+ # must be multiple of 1024, min 10240
+ out_buffer = FFI::MemoryPointer.new(API::ReparseDataBuffer.size)
+ device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, out_buffer)
+
+ reparse_data = API::ReparseDataBuffer.new(out_buffer)
+ offset = reparse_data[:print_name_offset]
+ length = reparse_data[:print_name_length]
+
+ result = reparse_data[:path_buffer].to_a[offset, length].pack('C*')
+ result.force_encoding('UTF-16LE').encode(Encoding.default_external)
+ end
end
diff --git a/lib/puppet/util/windows/process.rb b/lib/puppet/util/windows/process.rb
index fc85119c3..165e90af0 100644
--- a/lib/puppet/util/windows/process.rb
+++ b/lib/puppet/util/windows/process.rb
@@ -8,6 +8,117 @@ module Puppet::Util::Windows::Process
extend ::Windows::Handle
extend ::Windows::Synchronize
+ module API
+ require 'ffi'
+ extend FFI::Library
+ ffi_convention :stdcall
+
+ ffi_lib 'kernel32'
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/ms683179(v=vs.85).aspx
+ # HANDLE WINAPI GetCurrentProcess(void);
+ attach_function :get_current_process, :GetCurrentProcess, [], :uint
+
+ # BOOL WINAPI CloseHandle(
+ # _In_ HANDLE hObject
+ # );
+ attach_function :close_handle, :CloseHandle, [:uint], :bool
+
+ ffi_lib 'advapi32'
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa379295(v=vs.85).aspx
+ # BOOL WINAPI OpenProcessToken(
+ # _In_ HANDLE ProcessHandle,
+ # _In_ DWORD DesiredAccess,
+ # _Out_ PHANDLE TokenHandle
+ # );
+ attach_function :open_process_token, :OpenProcessToken,
+ [:uint, :uint, :pointer], :bool
+
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa379261(v=vs.85).aspx
+ # typedef struct _LUID {
+ # DWORD LowPart;
+ # LONG HighPart;
+ # } LUID, *PLUID;
+ class LUID < FFI::Struct
+ layout :low_part, :uint,
+ :high_part, :int
+ end
+
+ # http://msdn.microsoft.com/en-us/library/Windows/desktop/aa379180(v=vs.85).aspx
+ # BOOL WINAPI LookupPrivilegeValue(
+ # _In_opt_ LPCTSTR lpSystemName,
+ # _In_ LPCTSTR lpName,
+ # _Out_ PLUID lpLuid
+ # );
+ attach_function :lookup_privilege_value, :LookupPrivilegeValueW,
+ [:buffer_in, :buffer_in, :pointer], :bool
+
+ Token_Information = enum(
+ :token_user, 1,
+ :token_groups,
+ :token_privileges,
+ :token_owner,
+ :token_primary_group,
+ :token_default_dacl,
+ :token_source,
+ :token_type,
+ :token_impersonation_level,
+ :token_statistics,
+ :token_restricted_sids,
+ :token_session_id,
+ :token_groups_and_privileges,
+ :token_session_reference,
+ :token_sandbox_inert,
+ :token_audit_policy,
+ :token_origin,
+ :token_elevation_type,
+ :token_linked_token,
+ :token_elevation,
+ :token_has_restrictions,
+ :token_access_information,
+ :token_virtualization_allowed,
+ :token_virtualization_enabled,
+ :token_integrity_level,
+ :token_ui_access,
+ :token_mandatory_policy,
+ :token_logon_sid,
+ :max_token_info_class
+ )
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa379263(v=vs.85).aspx
+ # typedef struct _LUID_AND_ATTRIBUTES {
+ # LUID Luid;
+ # DWORD Attributes;
+ # } LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;
+ class LUID_And_Attributes < FFI::Struct
+ layout :luid, LUID,
+ :attributes, :uint
+ end
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa379630(v=vs.85).aspx
+ # typedef struct _TOKEN_PRIVILEGES {
+ # DWORD PrivilegeCount;
+ # LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
+ # } TOKEN_PRIVILEGES, *PTOKEN_PRIVILEGES;
+ class Token_Privileges < FFI::Struct
+ layout :privilege_count, :uint,
+ :privileges, [LUID_And_Attributes, 1] # placeholder for offset
+ end
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx
+ # BOOL WINAPI GetTokenInformation(
+ # _In_ HANDLE TokenHandle,
+ # _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
+ # _Out_opt_ LPVOID TokenInformation,
+ # _In_ DWORD TokenInformationLength,
+ # _Out_ PDWORD ReturnLength
+ # );
+ attach_function :get_token_information, :GetTokenInformation,
+ [:uint, Token_Information, :pointer, :uint, :pointer ], :bool
+ end
+
def execute(command, arguments, stdin, stdout, stderr)
Process.create( :command_line => command, :startup_info => {:stdin => stdin, :stdout => stdout, :stderr => stderr}, :close_handles => false )
end
@@ -33,4 +144,92 @@ module Puppet::Util::Windows::Process
exit_status
end
module_function :wait_process
+
+ def get_current_process
+ # this pseudo-handle does not require closing per MSDN docs
+ API.get_current_process
+ end
+ module_function :get_current_process
+
+ def open_process_token(handle, desired_access)
+ token_handle_ptr = FFI::MemoryPointer.new(:uint, 1)
+ result = API.open_process_token(handle, desired_access, token_handle_ptr)
+ if !result
+ raise Puppet::Util::Windows::Error.new(
+ "OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{token_handle_ptr})")
+ end
+
+ begin
+ yield token_handle = token_handle_ptr.read_uint
+ ensure
+ API.close_handle(token_handle)
+ end
+ end
+ module_function :open_process_token
+
+ def lookup_privilege_value(name, system_name = '')
+ luid = FFI::MemoryPointer.new(API::LUID.size)
+ result = API.lookup_privilege_value(WideString.new(system_name),
+ WideString.new(name.to_s), luid)
+
+ return API::LUID.new(luid) if result
+ raise Puppet::Util::Windows::Error.new(
+ "LookupPrivilegeValue(#{system_name}, #{name}, #{luid})")
+ end
+ module_function :lookup_privilege_value
+
+ def get_token_information(token_handle, token_information)
+ # to determine buffer size
+ return_length_ptr = FFI::MemoryPointer.new(:uint, 1)
+ result = API.get_token_information(token_handle, token_information, nil, 0, return_length_ptr)
+ return_length = return_length_ptr.read_uint
+
+ if return_length <= 0
+ raise Puppet::Util::Windows::Error.new(
+ "GetTokenInformation(#{token_handle}, #{token_information}, nil, 0, #{return_length_ptr})")
+ end
+
+ # re-call API with properly sized buffer for all results
+ token_information_buf = FFI::MemoryPointer.new(return_length)
+ result = API.get_token_information(token_handle, token_information,
+ token_information_buf, return_length, return_length_ptr)
+
+ if !result
+ raise Puppet::Util::Windows::Error.new(
+ "GetTokenInformation(#{token_handle}, #{token_information}, #{token_information_buf}, " +
+ "#{return_length}, #{return_length_ptr})")
+ end
+
+ raw_privileges = API::Token_Privileges.new(token_information_buf)
+ privileges = { :count => raw_privileges[:privilege_count], :privileges => [] }
+
+ offset = token_information_buf + API::Token_Privileges.offset_of(:privileges)
+ privilege_ptr = FFI::Pointer.new(API::LUID_And_Attributes, offset)
+
+ # extract each instance of LUID_And_Attributes
+ 0.upto(privileges[:count] - 1) do |i|
+ privileges[:privileges] << API::LUID_And_Attributes.new(privilege_ptr[i])
+ end
+
+ privileges
+ end
+ module_function :get_token_information
+
+ TOKEN_ALL_ACCESS = 0xF01FF
+ ERROR_NO_SUCH_PRIVILEGE = 1313
+ def process_privilege_symlink?
+ handle = get_current_process
+ open_process_token(handle, TOKEN_ALL_ACCESS) do |token_handle|
+ luid = lookup_privilege_value('SeCreateSymbolicLinkPrivilege')
+ token_info = get_token_information(token_handle, :token_privileges)
+ token_info[:privileges].any? { |p| p[:luid].values == luid.values }
+ end
+ rescue Puppet::Util::Windows::Error => e
+ if e.code == ERROR_NO_SUCH_PRIVILEGE
+ false # pre-Vista
+ else
+ raise e
+ end
+ end
+ module_function :process_privilege_symlink?
end
diff --git a/lib/puppet/util/windows/root_certs.rb b/lib/puppet/util/windows/root_certs.rb
index 4eae0a540..7988ab832 100644
--- a/lib/puppet/util/windows/root_certs.rb
+++ b/lib/puppet/util/windows/root_certs.rb
@@ -1,17 +1,16 @@
require 'puppet/util/windows'
require 'openssl'
-require 'Win32API'
-require 'windows/msvcrt/buffer'
+require 'ffi'
# Represents a collection of trusted root certificates.
#
# @api public
class Puppet::Util::Windows::RootCerts
include Enumerable
+ extend FFI::Library
- CertOpenSystemStore = Win32API.new('crypt32', 'CertOpenSystemStore', ['L','P'], 'L')
- CertEnumCertificatesInStore = Win32API.new('crypt32', 'CertEnumCertificatesInStore', ['L', 'L'], 'L')
- CertCloseStore = Win32API.new('crypt32', 'CertCloseStore', ['L', 'L'], 'B')
+ typedef :ulong, :dword
+ typedef :uintptr_t, :handle
def initialize(roots)
@roots = roots
@@ -24,10 +23,6 @@ class Puppet::Util::Windows::RootCerts
@roots.each {|cert| yield cert}
end
- class << self
- include Windows::MSVCRT::Buffer
- end
-
# Returns a new instance.
# @return [Puppet::Util::Windows::RootCerts] object constructed from current root certificates
def self.instance
@@ -43,34 +38,12 @@ class Puppet::Util::Windows::RootCerts
# This is based on a patch submitted to openssl:
# http://www.mail-archive.com/openssl-dev@openssl.org/msg26958.html
- context = 0
- store = CertOpenSystemStore.call(0, "ROOT")
+ ptr = FFI::Pointer::NULL
+ store = CertOpenSystemStoreA(nil, "ROOT")
begin
- while (context = CertEnumCertificatesInStore.call(store, context) and context != 0)
- # 466 typedef struct _CERT_CONTEXT {
- # 467 DWORD dwCertEncodingType;
- # 468 BYTE *pbCertEncoded;
- # 469 DWORD cbCertEncoded;
- # 470 PCERT_INFO pCertInfo;
- # 471 HCERTSTORE hCertStore;
- # 472 } CERT_CONTEXT, *PCERT_CONTEXT;
-
- # buffer to hold struct above
- ctx_buf = 0.chr * 5 * 8
-
- # copy from win to ruby
- memcpy(ctx_buf, context, ctx_buf.size)
-
- # unpack structure
- arr = ctx_buf.unpack('LLLLL')
-
- # create buf of length cbCertEncoded
- cert_buf = 0.chr * arr[2]
-
- # copy pbCertEncoded from win to ruby
- memcpy(cert_buf, arr[1], cert_buf.length)
-
- # create a cert
+ while (ptr = CertEnumCertificatesInStore(store, ptr)) and not ptr.null?
+ context = CERT_CONTEXT.new(ptr)
+ cert_buf = context[:pbCertEncoded].read_bytes(context[:cbCertEncoded])
begin
certs << OpenSSL::X509::Certificate.new(cert_buf)
rescue => detail
@@ -78,9 +51,51 @@ class Puppet::Util::Windows::RootCerts
end
end
ensure
- CertCloseStore.call(store, 0)
+ CertCloseStore(store, 0)
end
certs
end
+
+ private
+
+ # typedef ULONG_PTR HCRYPTPROV_LEGACY;
+ # typedef void *HCERTSTORE;
+
+ class CERT_CONTEXT < FFI::Struct
+ layout(
+ :dwCertEncodingType, :dword,
+ :pbCertEncoded, :pointer,
+ :cbCertEncoded, :dword,
+ :pCertInfo, :pointer,
+ :hCertStore, :handle
+ )
+ end
+
+ # HCERTSTORE
+ # WINAPI
+ # CertOpenSystemStoreA(
+ # __in_opt HCRYPTPROV_LEGACY hProv,
+ # __in LPCSTR szSubsystemProtocol
+ # );
+ ffi_lib :crypt32
+ attach_function :CertOpenSystemStoreA, [:pointer, :string], :handle
+
+ # PCCERT_CONTEXT
+ # WINAPI
+ # CertEnumCertificatesInStore(
+ # __in HCERTSTORE hCertStore,
+ # __in_opt PCCERT_CONTEXT pPrevCertContext
+ # );
+ ffi_lib :crypt32
+ attach_function :CertEnumCertificatesInStore, [:handle, :pointer], :pointer
+
+ # BOOL
+ # WINAPI
+ # CertCloseStore(
+ # __in_opt HCERTSTORE hCertStore,
+ # __in DWORD dwFlags
+ # );
+ ffi_lib :crypt32
+ attach_function :CertCloseStore, [:handle, :dword], :bool
end
diff --git a/lib/puppet/util/windows/security.rb b/lib/puppet/util/windows/security.rb
index 92df1b746..dfd7ea931 100644
--- a/lib/puppet/util/windows/security.rb
+++ b/lib/puppet/util/windows/security.rb
@@ -53,6 +53,8 @@
# enables Puppet to detect when file/dirs are out-of-sync,
# especially those that Puppet did not create, but is attempting
# to manage.
+# * A special case of this is S_ISYSTEM_MISSING, which is set when the
+# SYSTEM permissions are *not* present on the DACL.
# * On Unix, the owner and group can be modified without changing the
# mode. But on Windows, an access control entry specifies which SID
# it applies to. As a result, the set_owner and set_group methods
@@ -61,6 +63,7 @@
require 'puppet/util/windows'
require 'pathname'
+require 'ffi'
require 'win32/security'
@@ -100,11 +103,13 @@ module Puppet::Util::Windows::Security
S_IRWXO = 0000007
S_ISVTX = 0001000
S_IEXTRA = 02000000 # represents an extra ace
+ S_ISYSTEM_MISSING = 04000000
# constants that are missing from Windows::Security
PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
NO_INHERITANCE = 0x0
+ SE_DACL_PROTECTED = 0x1000
# Set the owner of the object referenced by +path+ to the specified
# +owner_sid+. The owner sid should be of the form "S-1-5-32-544"
@@ -112,9 +117,12 @@ module Puppet::Util::Windows::Security
# SE_RESTORE_NAME privilege in their process token can overwrite the
# object's owner to something other than the current user.
def set_owner(owner_sid, path)
- old_sid = get_owner(path)
+ sd = get_security_descriptor(path)
- change_sid(old_sid, owner_sid, OWNER_SECURITY_INFORMATION, path)
+ if owner_sid != sd.owner
+ sd.owner = owner_sid
+ set_security_descriptor(path, sd)
+ end
end
# Get the owner of the object referenced by +path+. The returned
@@ -125,7 +133,7 @@ module Puppet::Util::Windows::Security
def get_owner(path)
return unless supports_acl?(path)
- get_sid(OWNER_SECURITY_INFORMATION, path)
+ get_security_descriptor(path).owner
end
# Set the owner of the object referenced by +path+ to the specified
@@ -134,9 +142,12 @@ module Puppet::Util::Windows::Security
# access to the object can change the group (regardless of whether
# the current user belongs to that group or not).
def set_group(group_sid, path)
- old_sid = get_group(path)
+ sd = get_security_descriptor(path)
- change_sid(old_sid, group_sid, GROUP_SECURITY_INFORMATION, path)
+ if group_sid != sd.group
+ sd.group = group_sid
+ set_security_descriptor(path, sd)
+ end
end
# Get the group of the object referenced by +path+. The returned
@@ -147,7 +158,7 @@ module Puppet::Util::Windows::Security
def get_group(path)
return unless supports_acl?(path)
- get_sid(GROUP_SECURITY_INFORMATION, path)
+ get_security_descriptor(path).group
end
def supports_acl?(path)
@@ -163,31 +174,6 @@ module Puppet::Util::Windows::Security
(flags.unpack('L')[0] & Windows::File::FILE_PERSISTENT_ACLS) != 0
end
- def change_sid(old_sid, new_sid, info, path)
- if old_sid != new_sid
- mode = get_mode(path)
-
- string_to_sid_ptr(new_sid) do |psid|
- with_privilege(SE_RESTORE_NAME) do
- open_file(path, WRITE_OWNER) do |handle|
- set_security_info(handle, info, psid)
- end
- end
- end
-
- # rebuild dacl now that sid has changed
- set_mode(mode, path)
- end
- end
-
- def get_sid(info, path)
- with_privilege(SE_BACKUP_NAME) do
- open_file(path, READ_CONTROL) do |handle|
- get_security_info(handle, info)
- end
- end
- end
-
def get_attributes(path)
attributes = GetFileAttributes(path)
@@ -222,6 +208,10 @@ module Puppet::Util::Windows::Security
(FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES) => S_IXOTH
}
+ def get_aces_for_path_by_sid(path, sid)
+ get_security_descriptor(path).dacl.select { |ace| ace.sid == sid }
+ end
+
# Get the mode of the object referenced by +path+. The returned
# integer value represents the POSIX-style read, write, and execute
# modes for the user, group, and other classes, e.g. 0640. Any user
@@ -231,58 +221,61 @@ module Puppet::Util::Windows::Security
def get_mode(path)
return unless supports_acl?(path)
- owner_sid = get_owner(path)
- group_sid = get_group(path)
well_known_world_sid = Win32::Security::SID::Everyone
well_known_nobody_sid = Win32::Security::SID::Nobody
+ well_known_system_sid = Win32::Security::SID::LocalSystem
- with_privilege(SE_BACKUP_NAME) do
- open_file(path, READ_CONTROL) do |handle|
- mode = 0
-
- get_dacl(handle).each do |ace|
- case ace[:sid]
- when owner_sid
- MASK_TO_MODE.each_pair do |k,v|
- if (ace[:mask] & k) == k
- mode |= (v << 6)
- end
- end
- when group_sid
- MASK_TO_MODE.each_pair do |k,v|
- if (ace[:mask] & k) == k
- mode |= (v << 3)
- end
- end
- when well_known_world_sid
- MASK_TO_MODE.each_pair do |k,v|
- if (ace[:mask] & k) == k
- mode |= (v << 6) | (v << 3) | v
- end
- end
- if File.directory?(path) and (ace[:mask] & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD)) == (FILE_WRITE_DATA | FILE_EXECUTE)
- mode |= S_ISVTX;
- end
- when well_known_nobody_sid
- if (ace[:mask] & FILE_APPEND_DATA).nonzero?
- mode |= S_ISVTX
- end
- else
- #puts "Warning, unable to map SID into POSIX mode: #{ace[:sid]}"
- mode |= S_IEXTRA
- end
+ mode = S_ISYSTEM_MISSING
- # if owner and group the same, then user and group modes are the OR of both
- if owner_sid == group_sid
- mode |= ((mode & S_IRWXG) << 3) | ((mode & S_IRWXU) >> 3)
- #puts "owner: #{group_sid}, 0x#{ace[:mask].to_s(16)}, #{mode.to_s(8)}"
+ sd = get_security_descriptor(path)
+ sd.dacl.each do |ace|
+ next if ace.inherit_only?
+
+ case ace.sid
+ when sd.owner
+ MASK_TO_MODE.each_pair do |k,v|
+ if (ace.mask & k) == k
+ mode |= (v << 6)
+ end
+ end
+ when sd.group
+ MASK_TO_MODE.each_pair do |k,v|
+ if (ace.mask & k) == k
+ mode |= (v << 3)
+ end
+ end
+ when well_known_world_sid
+ MASK_TO_MODE.each_pair do |k,v|
+ if (ace.mask & k) == k
+ mode |= (v << 6) | (v << 3) | v
end
end
+ if File.directory?(path) && (ace.mask & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD)) == (FILE_WRITE_DATA | FILE_EXECUTE)
+ mode |= S_ISVTX;
+ end
+ when well_known_nobody_sid
+ if (ace.mask & FILE_APPEND_DATA).nonzero?
+ mode |= S_ISVTX
+ end
+ when well_known_system_sid
+ else
+ #puts "Warning, unable to map SID into POSIX mode: #{ace.sid}"
+ mode |= S_IEXTRA
+ end
- #puts "get_mode: #{mode.to_s(8)}"
- mode
+ if ace.sid == well_known_system_sid
+ mode &= ~S_ISYSTEM_MISSING
+ end
+
+ # if owner and group the same, then user and group modes are the OR of both
+ if sd.owner == sd.group
+ mode |= ((mode & S_IRWXG) << 3) | ((mode & S_IRWXU) >> 3)
+ #puts "owner: #{sd.group}, 0x#{ace.mask.to_s(16)}, #{mode.to_s(8)}"
end
end
+
+ #puts "get_mode: #{mode.to_s(8)}"
+ mode
end
MODE_TO_MASK = {
@@ -305,15 +298,16 @@ module Puppet::Util::Windows::Security
# privileges in their process token can change the mode for objects
# that they do not have read and write access to.
def set_mode(mode, path, protected = true)
- owner_sid = get_owner(path)
- group_sid = get_group(path)
+ sd = get_security_descriptor(path)
well_known_world_sid = Win32::Security::SID::Everyone
well_known_nobody_sid = Win32::Security::SID::Nobody
+ well_known_system_sid = Win32::Security::SID::LocalSystem
owner_allow = STANDARD_RIGHTS_ALL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES
group_allow = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | SYNCHRONIZE
other_allow = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | SYNCHRONIZE
nobody_allow = 0
+ system_allow = 0
MODE_TO_MASK.each do |k,v|
if ((mode >> 6) & k) == k
@@ -331,22 +325,29 @@ module Puppet::Util::Windows::Security
nobody_allow |= FILE_APPEND_DATA;
end
+ # caller is NOT managing SYSTEM by using group or owner, so set to FULL
+ if ! [sd.owner, sd.group].include? well_known_system_sid
+ # we don't check S_ISYSTEM_MISSING bit, but automatically carry over existing SYSTEM perms
+ # by default set SYSTEM perms to full
+ system_allow = FILE_ALL_ACCESS
+ end
+
isdir = File.directory?(path)
if isdir
if (mode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)
owner_allow |= FILE_DELETE_CHILD
end
- if (mode & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) and (mode & S_ISVTX) == 0
+ if (mode & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) && (mode & S_ISVTX) == 0
group_allow |= FILE_DELETE_CHILD
end
- if (mode & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) and (mode & S_ISVTX) == 0
+ if (mode & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) && (mode & S_ISVTX) == 0
other_allow |= FILE_DELETE_CHILD
end
end
# if owner and group the same, then map group permissions to the one owner ACE
- isownergroup = owner_sid == group_sid
+ isownergroup = sd.owner == sd.group
if isownergroup
owner_allow |= group_allow
end
@@ -357,64 +358,37 @@ module Puppet::Util::Windows::Security
remove_attributes(path, FILE_ATTRIBUTE_READONLY)
end
- set_acl(path, protected) do |acl|
- #puts "ace: owner #{owner_sid}, mask 0x#{owner_allow.to_s(16)}"
- add_access_allowed_ace(acl, owner_allow, owner_sid)
-
- unless isownergroup
- #puts "ace: group #{group_sid}, mask 0x#{group_allow.to_s(16)}"
- add_access_allowed_ace(acl, group_allow, group_sid)
- end
-
- #puts "ace: other #{well_known_world_sid}, mask 0x#{other_allow.to_s(16)}"
- add_access_allowed_ace(acl, other_allow, well_known_world_sid)
+ dacl = Puppet::Util::Windows::AccessControlList.new
+ dacl.allow(sd.owner, owner_allow)
+ unless isownergroup
+ dacl.allow(sd.group, group_allow)
+ end
+ dacl.allow(well_known_world_sid, other_allow)
+ dacl.allow(well_known_nobody_sid, nobody_allow)
- #puts "ace: nobody #{well_known_nobody_sid}, mask 0x#{nobody_allow.to_s(16)}"
- add_access_allowed_ace(acl, nobody_allow, well_known_nobody_sid)
+ # TODO: system should be first?
+ dacl.allow(well_known_system_sid, system_allow)
- # add inherit-only aces for child dirs and files that are created within the dir
- if isdir
- inherit = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE
- add_access_allowed_ace(acl, owner_allow, Win32::Security::SID::CreatorOwner, inherit)
- add_access_allowed_ace(acl, group_allow, Win32::Security::SID::CreatorGroup, inherit)
+ # add inherit-only aces for child dirs and files that are created within the dir
+ if isdir
+ inherit = INHERIT_ONLY_ACE | CONTAINER_INHERIT_ACE
+ dacl.allow(Win32::Security::SID::CreatorOwner, owner_allow, inherit)
+ dacl.allow(Win32::Security::SID::CreatorGroup, group_allow, inherit)
- inherit = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
- add_access_allowed_ace(acl, owner_allow & ~FILE_EXECUTE, Win32::Security::SID::CreatorOwner, inherit)
- add_access_allowed_ace(acl, group_allow & ~FILE_EXECUTE, Win32::Security::SID::CreatorGroup, inherit)
- end
+ inherit = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE
+ dacl.allow(Win32::Security::SID::CreatorOwner, owner_allow & ~FILE_EXECUTE, inherit)
+ dacl.allow(Win32::Security::SID::CreatorGroup, group_allow & ~FILE_EXECUTE, inherit)
end
+ new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, dacl, protected)
+ set_security_descriptor(path, new_sd)
+
nil
end
- # setting DACL requires both READ_CONTROL and WRITE_DACL access rights,
- # and their respective privileges, SE_BACKUP_NAME and SE_RESTORE_NAME.
- def set_acl(path, protected = true)
- with_privilege(SE_BACKUP_NAME) do
- with_privilege(SE_RESTORE_NAME) do
- open_file(path, READ_CONTROL | WRITE_DAC) do |handle|
- acl = 0.chr * 1024 # This can be increased later as needed
-
- unless InitializeAcl(acl, acl.size, ACL_REVISION)
- raise Puppet::Util::Windows::Error.new("Failed to initialize ACL")
- end
-
- raise Puppet::Util::Windows::Error.new("Invalid DACL") unless IsValidAcl(acl)
-
- yield acl
-
- # protected means the object does not inherit aces from its parent
- info = DACL_SECURITY_INFORMATION
- info |= protected ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION
-
- # set the DACL
- set_security_info(handle, info, acl)
- end
- end
- end
- end
+ def add_access_allowed_ace(acl, mask, sid, inherit = nil)
+ inherit ||= NO_INHERITANCE
- def add_access_allowed_ace(acl, mask, sid, inherit = NO_INHERITANCE)
string_to_sid_ptr(sid) do |sid_ptr|
raise Puppet::Util::Windows::Error.new("Invalid SID") unless IsValidSid(sid_ptr)
@@ -434,126 +408,68 @@ module Puppet::Util::Windows::Security
end
end
- def get_dacl(handle)
- get_dacl_ptr(handle) do |dacl_ptr|
- # REMIND: need to handle NULL DACL
- raise Puppet::Util::Windows::Error.new("Invalid DACL") unless IsValidAcl(dacl_ptr)
-
- # ACL structure, size and count are the important parts. The
- # size includes both the ACL structure and all the ACEs.
+ def parse_dacl(dacl_ptr)
+ # REMIND: need to handle NULL DACL
+ raise Puppet::Util::Windows::Error.new("Invalid DACL") unless IsValidAcl(dacl_ptr)
+
+ # ACL structure, size and count are the important parts. The
+ # size includes both the ACL structure and all the ACEs.
+ #
+ # BYTE AclRevision
+ # BYTE Padding1
+ # WORD AclSize
+ # WORD AceCount
+ # WORD Padding2
+ acl_buf = 0.chr * 8
+ memcpy(acl_buf, dacl_ptr, acl_buf.size)
+ ace_count = acl_buf.unpack('CCSSS')[3]
+
+ dacl = Puppet::Util::Windows::AccessControlList.new
+
+ # deny all
+ return dacl if ace_count == 0
+
+ 0.upto(ace_count - 1) do |i|
+ ace_ptr = [0].pack('L')
+
+ next unless GetAce(dacl_ptr, i, ace_ptr)
+
+ # ACE structures vary depending on the type. All structures
+ # begin with an ACE header, which specifies the type, flags
+ # and size of what follows. We are only concerned with
+ # ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACEs, which have the
+ # same structure:
#
- # BYTE AclRevision
- # BYTE Padding1
- # WORD AclSize
- # WORD AceCount
- # WORD Padding2
- acl_buf = 0.chr * 8
- memcpy(acl_buf, dacl_ptr, acl_buf.size)
- ace_count = acl_buf.unpack('CCSSS')[3]
-
- dacl = []
-
- # deny all
- return dacl if ace_count == 0
-
- 0.upto(ace_count - 1) do |i|
- ace_ptr = [0].pack('L')
-
- next unless GetAce(dacl_ptr, i, ace_ptr)
-
- # ACE structures vary depending on the type. All structures
- # begin with an ACE header, which specifies the type, flags
- # and size of what follows. We are only concerned with
- # ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACEs, which have the
- # same structure:
- #
- # BYTE C AceType
- # BYTE C AceFlags
- # WORD S AceSize
- # DWORD L ACCESS_MASK
- # DWORD L Sid
- # .. ...
- # DWORD L Sid
-
- ace_buf = 0.chr * 8
- memcpy(ace_buf, ace_ptr.unpack('L')[0], ace_buf.size)
-
- ace_type, ace_flags, size, mask = ace_buf.unpack('CCSL')
-
- # skip aces that only serve to propagate inheritance
- next if (ace_flags & INHERIT_ONLY_ACE).nonzero?
-
- case ace_type
- when ACCESS_ALLOWED_ACE_TYPE
- sid_ptr = ace_ptr.unpack('L')[0] + 8 # address of ace_ptr->SidStart
- raise Puppet::Util::Windows::Error.new("Failed to read DACL, invalid SID") unless IsValidSid(sid_ptr)
- sid = sid_ptr_to_string(sid_ptr)
- dacl << {:sid => sid, :type => ace_type, :mask => mask}
- else
- Puppet.warning "Unsupported access control entry type: 0x#{ace_type.to_s(16)}"
- end
+ # BYTE C AceType
+ # BYTE C AceFlags
+ # WORD S AceSize
+ # DWORD L ACCESS_MASK
+ # DWORD L Sid
+ # .. ...
+ # DWORD L Sid
+
+ ace_buf = 0.chr * 8
+ memcpy(ace_buf, ace_ptr.unpack('L')[0], ace_buf.size)
+
+ ace_type, ace_flags, size, mask = ace_buf.unpack('CCSL')
+
+ case ace_type
+ when ACCESS_ALLOWED_ACE_TYPE
+ sid_ptr = ace_ptr.unpack('L')[0] + 8 # address of ace_ptr->SidStart
+ raise Puppet::Util::Windows::Error.new("Failed to read DACL, invalid SID") unless IsValidSid(sid_ptr)
+ sid = sid_ptr_to_string(sid_ptr)
+ dacl.allow(sid, mask, ace_flags)
+ when ACCESS_DENIED_ACE_TYPE
+ sid_ptr = ace_ptr.unpack('L')[0] + 8 # address of ace_ptr->SidStart
+ raise Puppet::Util::Windows::Error.new("Failed to read DACL, invalid SID") unless IsValidSid(sid_ptr)
+ sid = sid_ptr_to_string(sid_ptr)
+ dacl.deny(sid, mask, ace_flags)
+ else
+ Puppet.warning "Unsupported access control entry type: 0x#{ace_type.to_s(16)}"
end
-
- dacl
end
- end
- def get_dacl_ptr(handle)
- dacl = [0].pack('L')
- sd = [0].pack('L')
-
- rv = GetSecurityInfo(
- handle,
- SE_FILE_OBJECT,
- DACL_SECURITY_INFORMATION,
- nil,
- nil,
- dacl, #dacl
- nil, #sacl
- sd) #sec desc
- raise Puppet::Util::Windows::Error.new("Failed to get DACL") unless rv == ERROR_SUCCESS
- begin
- yield dacl.unpack('L')[0]
- ensure
- LocalFree(sd.unpack('L')[0])
- end
- end
-
- # Set the security info on the specified handle.
- def set_security_info(handle, info, ptr)
- rv = SetSecurityInfo(
- handle,
- SE_FILE_OBJECT,
- info,
- (info & OWNER_SECURITY_INFORMATION) == OWNER_SECURITY_INFORMATION ? ptr : nil,
- (info & GROUP_SECURITY_INFORMATION) == GROUP_SECURITY_INFORMATION ? ptr : nil,
- (info & DACL_SECURITY_INFORMATION) == DACL_SECURITY_INFORMATION ? ptr : nil,
- nil)
- raise Puppet::Util::Windows::Error.new("Failed to set security information") unless rv == ERROR_SUCCESS
- end
-
- # Get the SID string, e.g. "S-1-5-32-544", for the specified handle
- # and type of information (owner, group).
- def get_security_info(handle, info)
- sid = [0].pack('L')
- sd = [0].pack('L')
-
- rv = GetSecurityInfo(
- handle,
- SE_FILE_OBJECT,
- info, # security info
- info == OWNER_SECURITY_INFORMATION ? sid : nil,
- info == GROUP_SECURITY_INFORMATION ? sid : nil,
- nil, #dacl
- nil, #sacl
- sd) #sec desc
- raise Puppet::Util::Windows::Error.new("Failed to get security information") unless rv == ERROR_SUCCESS
-
- begin
- return sid_ptr_to_string(sid.unpack('L')[0])
- ensure
- LocalFree(sd.unpack('L')[0])
- end
+ dacl
end
# Open an existing file with the specified access mode, and execute a
@@ -565,7 +481,7 @@ module Puppet::Util::Windows::Security
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, # security_attributes
OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
0) # template
raise Puppet::Util::Windows::Error.new("Failed to open '#{path}'") if handle == INVALID_HANDLE_VALUE
begin
@@ -620,4 +536,114 @@ module Puppet::Util::Windows::Security
CloseHandle(token)
end
end
+
+ def get_security_descriptor(path)
+ sd = nil
+
+ with_privilege(SE_BACKUP_NAME) do
+ open_file(path, READ_CONTROL) do |handle|
+ owner_sid = [0].pack('L')
+ group_sid = [0].pack('L')
+ dacl = [0].pack('L')
+ ppsd = [0].pack('L')
+
+ rv = GetSecurityInfo(
+ handle,
+ SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+ owner_sid,
+ group_sid,
+ dacl,
+ nil, #sacl
+ ppsd) #sec desc
+ raise Puppet::Util::Windows::Error.new("Failed to get security information") unless rv == ERROR_SUCCESS
+
+ begin
+ owner = sid_ptr_to_string(owner_sid.unpack('L')[0])
+ group = sid_ptr_to_string(group_sid.unpack('L')[0])
+
+ control = FFI::MemoryPointer.new(:uint16, 1)
+ revision = FFI::MemoryPointer.new(:uint32, 1)
+ ffsd = FFI::Pointer.new(ppsd.unpack('L')[0])
+
+ if ! API.get_security_descriptor_control(ffsd, control, revision)
+ raise Puppet::Util::Windows::Error.new("Failed to get security descriptor control")
+ end
+
+ protect = (control.read_uint16 & SE_DACL_PROTECTED) == SE_DACL_PROTECTED
+
+ dacl = parse_dacl(dacl.unpack('L')[0])
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(owner, group, dacl, protect)
+ ensure
+ LocalFree(ppsd.unpack('L')[0])
+ end
+ end
+ end
+
+ sd
+ end
+
+ # setting DACL requires both READ_CONTROL and WRITE_DACL access rights,
+ # and their respective privileges, SE_BACKUP_NAME and SE_RESTORE_NAME.
+ def set_security_descriptor(path, sd)
+ # REMIND: FFI
+ acl = 0.chr * 1024 # This can be increased later as neede
+ unless InitializeAcl(acl, acl.size, ACL_REVISION)
+ raise Puppet::Util::Windows::Error.new("Failed to initialize ACL")
+ end
+
+ raise Puppet::Util::Windows::Error.new("Invalid DACL") unless IsValidAcl(acl)
+
+ with_privilege(SE_BACKUP_NAME) do
+ with_privilege(SE_RESTORE_NAME) do
+ open_file(path, READ_CONTROL | WRITE_DAC | WRITE_OWNER) do |handle|
+ string_to_sid_ptr(sd.owner) do |ownersid|
+ string_to_sid_ptr(sd.group) do |groupsid|
+ sd.dacl.each do |ace|
+ case ace.type
+ when ACCESS_ALLOWED_ACE_TYPE
+ #puts "ace: allow, sid #{sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}"
+ add_access_allowed_ace(acl, ace.mask, ace.sid, ace.flags)
+ when ACCESS_DENIED_ACE_TYPE
+ #puts "ace: deny, sid #{sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}"
+ add_access_denied_ace(acl, ace.mask, ace.sid)
+ else
+ raise "We should never get here"
+ # TODO: this should have been a warning in an earlier commit
+ end
+ end
+
+ # protected means the object does not inherit aces from its parent
+ flags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION
+ flags |= sd.protect ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION
+
+ rv = SetSecurityInfo(handle,
+ SE_FILE_OBJECT,
+ flags,
+ ownersid,
+ groupsid,
+ acl,
+ nil)
+ raise Puppet::Util::Windows::Error.new("Failed to set security information") unless rv == ERROR_SUCCESS
+ end
+ end
+ end
+ end
+ end
+ end
+
+ module API
+ extend FFI::Library
+ ffi_lib 'kernel32'
+ ffi_convention :stdcall
+
+ # typedef WORD SECURITY_DESCRIPTOR_CONTROL, *PSECURITY_DESCRIPTOR_CONTROL;
+ # BOOL WINAPI GetSecurityDescriptorControl(
+ # _In_ PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ # _Out_ PSECURITY_DESCRIPTOR_CONTROL pControl,
+ # _Out_ LPDWORD lpdwRevision
+ # );
+ ffi_lib :advapi32
+ attach_function :get_security_descriptor_control, :GetSecurityDescriptorControl, [:pointer, :pointer, :pointer], :bool
+ end
end
diff --git a/lib/puppet/util/windows/security_descriptor.rb b/lib/puppet/util/windows/security_descriptor.rb
new file mode 100644
index 000000000..9c95cee6d
--- /dev/null
+++ b/lib/puppet/util/windows/security_descriptor.rb
@@ -0,0 +1,62 @@
+# Windows Security Descriptor
+#
+# Represents a security descriptor that can be applied to any Windows securable
+# object, e.g. file, registry key, service, etc. It consists of an owner, group,
+# flags, DACL, and SACL. The SACL is not currently supported, though it has the
+# same layout as a DACL.
+#
+# @see http://msdn.microsoft.com/en-us/library/windows/desktop/aa379563(v=vs.85).aspx
+# @api private
+class Puppet::Util::Windows::SecurityDescriptor
+ require 'puppet/util/windows/security'
+ include Puppet::Util::Windows::SID
+
+ attr_reader :owner, :group, :dacl
+ attr_accessor :protect
+
+ # Construct a security descriptor
+ #
+ # @param owner [String] The SID of the owner, e.g. 'S-1-5-18'
+ # @param group [String] The SID of the group
+ # @param dacl [AccessControlList] The ACL specifying the rights granted to
+ # each user for accessing the object that the security descriptor refers to.
+ # @param protect [Boolean] If true, then inheritable access control
+ # entries will be blocked, and not applied to the object.
+ def initialize(owner, group, dacl, protect = false)
+ @owner = owner
+ @group = group
+ @dacl = dacl
+ @protect = protect
+ end
+
+ # Set the owner. Non-inherited access control entries assigned to the
+ # current owner will be assigned to the new owner.
+ #
+ # @param new_owner [String] The SID of the new owner, e.g. 'S-1-5-18'
+ def owner=(new_owner)
+ if @owner != new_owner
+ @dacl.reassign!(@owner, new_owner)
+ @owner = new_owner
+ end
+ end
+
+ # Set the group. Non-inherited access control entries assigned to the
+ # current group will be assigned to the new group.
+ #
+ # @param new_group [String] The SID of the new group, e.g. 'S-1-0-0'
+ def group=(new_group)
+ if @group != new_group
+ @dacl.reassign!(@group, new_group)
+ @group = new_group
+ end
+ end
+
+ def inspect
+ str = sid_to_name(owner)
+ str << "\n"
+ str << sid_to_name(group)
+ str << "\n"
+ str << @dacl.inspect
+ str
+ end
+end
diff --git a/lib/puppet/util/windows/sid.rb b/lib/puppet/util/windows/sid.rb
index cd321eacb..90b48d933 100644
--- a/lib/puppet/util/windows/sid.rb
+++ b/lib/puppet/util/windows/sid.rb
@@ -20,17 +20,39 @@ module Puppet::Util::Windows
# 'BUILTIN\Administrators', or 'S-1-5-32-544', and will return the
# SID. Returns nil if the account doesn't exist.
def name_to_sid(name)
+ sid = name_to_sid_object(name)
+
+ sid ? sid.to_s : nil
+ end
+
+ # Convert an account name, e.g. 'Administrators' into a SID object,
+ # e.g. 'S-1-5-32-544'. The name can be specified as 'Administrators',
+ # 'BUILTIN\Administrators', or 'S-1-5-32-544', and will return the
+ # SID object. Returns nil if the account doesn't exist.
+ def name_to_sid_object(name)
# Apparently, we accept a symbol..
- name = name.to_s if name
+ name = name.to_s.strip if name
- # if it's in SID string form, return it, otherwise, lookup sid
- is_sid = Win32::Security::SID.string_to_sid(name) rescue nil
+ # if it's in SID string form, convert to user
+ parsed_sid = Win32::Security::SID.string_to_sid(name) rescue nil
- is_sid ? name : Win32::Security::SID.new(name).to_s
+ parsed_sid ? Win32::Security::SID.new(parsed_sid) : Win32::Security::SID.new(name)
rescue
nil
end
+ # Converts an octet string array of bytes to a SID object,
+ # e.g. [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0] is the representation for
+ # S-1-5-18, the local 'SYSTEM' account.
+ # Raises an Error for nil or non-array input.
+ def octet_string_to_sid_object(bytes)
+ if !bytes || !bytes.respond_to?('pack') || bytes.empty?
+ raise Puppet::Util::Windows::Error.new("Octet string must be an array of bytes")
+ end
+
+ Win32::Security::SID.new(bytes.pack('C*'))
+ end
+
# Convert a SID string, e.g. "S-1-5-32-544" to a name,
# e.g. 'BUILTIN\Administrators'. Returns nil if an account
# for that SID does not exist.
diff --git a/lib/puppet/util/yaml.rb b/lib/puppet/util/yaml.rb
index d0c37ae77..246080843 100644
--- a/lib/puppet/util/yaml.rb
+++ b/lib/puppet/util/yaml.rb
@@ -9,8 +9,9 @@ module Puppet::Util::Yaml
class YamlLoadError < Puppet::Error; end
- def self.load_file(filename)
- YAML.load_file(filename)
+ def self.load_file(filename, default_value = false)
+ yaml = YAML.load_file(filename)
+ yaml || default_value
rescue *YamlLoadExceptions => detail
raise YamlLoadError.new(detail.message, detail)
end
diff --git a/lib/puppet/version.rb b/lib/puppet/version.rb
index 9f9f738be..b416a864b 100644
--- a/lib/puppet/version.rb
+++ b/lib/puppet/version.rb
@@ -7,7 +7,7 @@
module Puppet
- PUPPETVERSION = '3.3.1'
+ PUPPETVERSION = '3.4.0'
##
# version is a public API method intended to always provide a fast and
@@ -81,7 +81,7 @@ module Puppet
# @return [String] for example: "1.6.14-6-gea42046" or nil if the VERSION
# file does not exist.
def self.read_version_file(path)
- if File.exists?(path)
+ if File.exist?(path)
File.read(path).chomp
end
end
diff --git a/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb b/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb
index 82ced05b7..f0cbc446b 100644
--- a/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb
+++ b/spec/fixtures/releases/jamtur01-apache/lib/puppet/provider/a2mod/debian.rb
@@ -16,6 +16,6 @@ Puppet::Type.type(:a2mod).provide(:debian) do
def exists?
mod= "/etc/apache2/mods-enabled/" + resource[:name] + ".load"
- File.exists?(mod)
+ Puppet::FileSystem::File.exist?(mod)
end
end
diff --git a/spec/fixtures/unit/indirector/hiera/global.yaml b/spec/fixtures/unit/indirector/data_binding/hiera/global.yaml
index 0853e0ec1..0853e0ec1 100644
--- a/spec/fixtures/unit/indirector/hiera/global.yaml
+++ b/spec/fixtures/unit/indirector/data_binding/hiera/global.yaml
diff --git a/spec/fixtures/unit/indirector/data_binding/hiera/invalid.yaml b/spec/fixtures/unit/indirector/data_binding/hiera/invalid.yaml
new file mode 100644
index 000000000..9a84fa87c
--- /dev/null
+++ b/spec/fixtures/unit/indirector/data_binding/hiera/invalid.yaml
@@ -0,0 +1 @@
+{ invalid:
diff --git a/spec/fixtures/unit/module/trailing-comma.json b/spec/fixtures/unit/module/trailing-comma.json
new file mode 100644
index 000000000..23750bfb2
--- /dev/null
+++ b/spec/fixtures/unit/module/trailing-comma.json
@@ -0,0 +1,24 @@
+{
+ "name": "puppetlabs/ruby",
+ "version": "0.0.2",
+ "summary": "Manage Ruby",
+ "source": "git@github.com/puppetlabs/puppetlabs-ruby.git",
+ "project_page": "http://github.com/puppetlabs/puppetlabs-ruby",
+ "author": "Puppet Labs",
+ "license": "Apache-2.0",
+ "operatingsystem_support": [
+ "RedHat",
+ "OpenSUSE",
+ "SLES",
+ "SLED",
+ "Debian",
+ "Ubuntu"
+ ],
+ "puppet_version": [
+ 2.7,
+ 3.0,
+ 3.1,
+ 3.2,
+ 3.3,
+ ],
+}
diff --git a/spec/fixtures/unit/util/monkey_patches/x509.pem b/spec/fixtures/unit/util/monkey_patches/x509.pem
new file mode 100644
index 000000000..f7ed7e0fc
--- /dev/null
+++ b/spec/fixtures/unit/util/monkey_patches/x509.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFmTCCA4GgAwIBAgIQea0WoUqgpa1Mc1j0BxMuZTANBgkqhkiG9w0BAQUFADBf
+MRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0
+MS0wKwYDVQQDEyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw
+HhcNMDEwNTA5MjMxOTIyWhcNMjEwNTA5MjMyODEzWjBfMRMwEQYKCZImiZPyLGQB
+GRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQDEyRNaWNy
+b3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDzXfqAZ9Rap6kMLJAg0DUIPHWEzbcHiZyJ2t7Ow2D6
+kWhanpRxKRh2fMLgyCV2lA5Y+gQ0Nubfr/eAuulYCyuT5Z0F43cikfc0ZDwikR1e
+4QmQvBT+/HVYGeF5tweSo66IWQjYnwfKA1j8aCltMtfSqMtL/OELSDJP5uu4rU/k
+XG8TlJnbldV126gat5SRtHdb9UgMj2p5fRRwBH1tr5D12nDYR7e/my9s5wW34RFg
+rHmRFHzF1qbk4X7Vw37lktI8ALU2gt554W3ztW74nzPJy1J9c5g224uha6KVl5uj
+3sJNJv8GlmclBsjnrOTuEjOVMZnINQhONMp5U9W1vmMyWUA2wKVOBE0921sHM+RY
+v+8/U2TYQlk1V/0PRXwkBE2e1jh0EZcikM5oRHSSb9VLb7CG48c2QqDQ/MHAWvmj
+YbkwR3GWChawkcBCle8Qfyhq4yofseTNAz93cQTHIPxJDx1FiKTXy36IrY4t7EXb
+xFEEySr87IaemhGXW97OU4jm4rf9rJXCKEDb7wSQ34EzOdmyRaUjhwalVYkxuwYt
+YA5BGH0fLrWXyxHrFdUkpZTvFRSJ/Utz+jJb/NEzAPlZYnAHMuouq0Ate8rdIWcb
+MJmPFqojqEHRsG4RmzbE3kB0nOFYZcFgHnpbOMiPuwQmfNQWQOW2a2yqhv0Av87B
+NQIDAQABo1EwTzALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQUDqyCYEBWJ5flJRP8KuEKU5VZ5KQwEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI
+hvcNAQEFBQADggIBAMURTQM6YN1dUhF3j7K7NsiyBb+0t6jYIJ1cEwO2HCL6BhM1
+tshj1JpHbyZX0lXxBLEmX9apUGigvNK4bszD6azfGc14rFl0rGY0NsQbPmw4TDMO
+MBINoyb+UVMA/69aToQNDx/kbQUuToVLjWwzb1TSZKu/UK99ejmgN+1jAw/8EwbO
+FjbUVDuVG1FiOuVNF9QFOZKaJ6hbqr3su77jIIlgcWxWs6UT0G0OI36VA+1oPfLY
+Y7hrTbboMLXhypRL96KqXZkwsj2nwlFsKCABJCcrSwC3nRFrcL6yEIK8DJto0I07
+JIeqmShynTNfWZC99d6TnjpiWjQ54ohVHbkGsMGJay3XacMZEjaE0Mmg2v8vaXiy
+5Xra69cMwPe9Yxe4ORM4ojZbe/KFVmodZGLBOOKqv1FmopT1EpxmIhBr8rcwki3y
+KfA9OxRDaKLxnCk3y844ICVtfGfzfiQSJAMIgUfspZ6X9RjXz7vV73aW7/3O21ad
+laBC+ZdY4dcxItNfWeY+biIA6kOEtiXb2fMIVmjAZGsdfOy2k6JiV24u2OdYj8Qx
+SSbd3ik1h/UwcXBbFDxpvYkSfesuo/7Yf56CWlIKK8FDK9kwiJ/IEPuJjeahhXUz
+fmye23MTZGJppS99ypZtn/gETTCSPW4hFCHJPeDD/YprnUr90aGdmUN3P7Da
+-----END CERTIFICATE-----
diff --git a/spec/integration/application/apply_spec.rb b/spec/integration/application/apply_spec.rb
index aa1f33d4f..43fb3cd66 100755
--- a/spec/integration/application/apply_spec.rb
+++ b/spec/integration/application/apply_spec.rb
@@ -26,7 +26,7 @@ describe "apply" do
puppet.apply
- File.should be_exist(file_to_create)
+ Puppet::FileSystem::File.exist?(file_to_create).should be_true
File.read(file_to_create).should == "my stuff"
end
end
diff --git a/spec/integration/application/doc_spec.rb b/spec/integration/application/doc_spec.rb
index 83dd39ba9..97ef026e7 100755
--- a/spec/integration/application/doc_spec.rb
+++ b/spec/integration/application/doc_spec.rb
@@ -40,7 +40,7 @@ describe Puppet::Application::Doc do
expect { puppet.run_command }.to exit_with 0
- File.should be_exist('doc')
+ Puppet::FileSystem::File.exist?('doc').should be_true
ensure
Dir.chdir(old_dir)
end
diff --git a/spec/integration/configurer_spec.rb b/spec/integration/configurer_spec.rb
index 355458f02..ddb044a0b 100755
--- a/spec/integration/configurer_spec.rb
+++ b/spec/integration/configurer_spec.rb
@@ -9,7 +9,9 @@ describe Puppet::Configurer do
describe "when downloading plugins" do
it "should use the :pluginsignore setting, split on whitespace, for ignoring remote files" do
resource = Puppet::Type.type(:notify).new :name => "yay"
- Puppet::Type.type(:file).expects(:new).with { |args| args[:ignore] == Puppet[:pluginsignore].split(/\s+/) }.returns resource
+ Puppet::Type.type(:file).expects(:new).at_most(2).with do |args|
+ args[:ignore] == Puppet[:pluginsignore].split(/\s+/)
+ end.returns resource
configurer = Puppet::Configurer.new
configurer.stubs(:download_plugins?).returns true
@@ -59,7 +61,7 @@ describe Puppet::Configurer do
file_mode = Puppet.features.microsoft_windows? ? '100644' : '100666'
- File.stat(Puppet[:lastrunfile]).mode.to_s(8).should == file_mode
+ Puppet::FileSystem::File.new(Puppet[:lastrunfile]).stat.mode.to_s(8).should == file_mode
summary = nil
File.open(Puppet[:lastrunfile], "r") do |fd|
diff --git a/spec/integration/data_binding.rb b/spec/integration/data_binding.rb
new file mode 100644
index 000000000..0e8db4dfa
--- /dev/null
+++ b/spec/integration/data_binding.rb
@@ -0,0 +1,100 @@
+require 'spec_helper'
+
+require 'puppet_spec/compiler'
+
+describe "Data binding" do
+ include PuppetSpec::Files
+ include PuppetSpec::Compiler
+
+ let(:dir) { tmpdir("puppetdir") }
+
+ before do
+ Puppet[:data_binding_terminus] = "hiera"
+ Puppet[:modulepath] = dir
+ end
+
+ it "looks up data from hiera" do
+ configure_hiera_for({
+ "testing::binding::value" => "the value",
+ "testing::binding::calling_class" => "%{calling_class}",
+ "testing::binding::calling_module" => "%{calling_module}"
+ })
+
+ create_manifest_in_module("testing", "binding.pp",
+ <<-MANIFEST)
+ class testing::binding($value,
+ $calling_class,
+ $calling_module) {}
+ MANIFEST
+
+ catalog = compile_to_catalog("include testing::binding")
+ resource = catalog.resource('Class[testing::binding]')
+
+ expect(resource[:value]).to eq("the value")
+ expect(resource[:calling_class]).to eq("testing::binding")
+ expect(resource[:calling_module]).to eq("testing")
+ end
+
+ it "works with the puppet backend configured, although it can't use it for lookup" do
+ configure_hiera_for_puppet
+ create_manifest_in_module("testing", "binding.pp",
+ <<-MANIFEST)
+ # lookup via the puppet backend to ensure it works
+ class testing::binding($value = hiera('variable')) {}
+ MANIFEST
+
+ create_manifest_in_module("testing", "data.pp",
+ <<-MANIFEST)
+ class testing::data {
+ $variable = "the value"
+ }
+ MANIFEST
+
+ catalog = compile_to_catalog("include testing::binding")
+ resource = catalog.resource('Class[testing::binding]')
+
+ expect(resource[:value]).to eq("the value")
+ end
+
+ def configure_hiera_for(data)
+ hiera_config_file = tmpfile("hiera.yaml")
+
+ File.open(hiera_config_file, 'w') do |f|
+ f.write("---
+ :yaml:
+ :datadir: #{dir}
+ :hierarchy: ['global']
+ :logger: 'noop'
+ :backends: ['yaml']
+ ")
+ end
+
+ File.open(File.join(dir, 'global.yaml'), 'w') do |f|
+ f.write(YAML.dump(data))
+ end
+
+ Puppet[:hiera_config] = hiera_config_file
+ end
+
+ def configure_hiera_for_puppet
+ hiera_config_file = tmpfile("hiera.yaml")
+
+ File.open(hiera_config_file, 'w') do |f|
+ f.write("---
+ :logger: 'noop'
+ :backends: ['puppet']
+ ")
+ end
+
+ Puppet[:hiera_config] = hiera_config_file
+ end
+
+ def create_manifest_in_module(module_name, name, manifest)
+ module_dir = File.join(dir, module_name, 'manifests')
+ FileUtils.mkdir_p(module_dir)
+
+ File.open(File.join(module_dir, name), 'w') do |f|
+ f.write(manifest)
+ end
+ end
+end
diff --git a/spec/integration/indirector/catalog/compiler_spec.rb b/spec/integration/indirector/catalog/compiler_spec.rb
index f5c16964f..be5a415bc 100755
--- a/spec/integration/indirector/catalog/compiler_spec.rb
+++ b/spec/integration/indirector/catalog/compiler_spec.rb
@@ -32,33 +32,36 @@ describe Puppet::Resource::Catalog::Compiler do
end
it "should filter out virtual resources when finding a catalog" do
- @one.virtual = true
- request = stub 'request', :name => "mynode"
+ Puppet[:node_terminus] = :memory
+ Puppet::Node.indirection.save(Puppet::Node.new("mynode"))
Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request)
- Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request)
Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog)
- Puppet::Resource::Catalog.indirection.find(request).resource_refs.should == [ @two.ref ]
+ @one.virtual = true
+
+ Puppet::Resource::Catalog.indirection.find("mynode").resource_refs.should == [ @two.ref ]
end
it "should not filter out exported resources when finding a catalog" do
- @one.exported = true
- request = stub 'request', :name => "mynode"
+ Puppet[:node_terminus] = :memory
+ Puppet::Node.indirection.save(Puppet::Node.new("mynode"))
Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request)
- Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request)
Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog)
- Puppet::Resource::Catalog.indirection.find(request).resource_refs.sort.should == [ @one.ref, @two.ref ]
+ @one.exported = true
+
+ Puppet::Resource::Catalog.indirection.find("mynode").resource_refs.sort.should == [ @one.ref, @two.ref ]
end
it "should filter out virtual exported resources when finding a catalog" do
- @one.exported = true
- @one.virtual = true
- request = stub 'request', :name => "mynode"
+ Puppet[:node_terminus] = :memory
+ Puppet::Node.indirection.save(Puppet::Node.new("mynode"))
Puppet::Resource::Catalog.indirection.terminus.stubs(:extract_facts_from_request)
- Puppet::Resource::Catalog.indirection.terminus.stubs(:node_from_request)
Puppet::Resource::Catalog.indirection.terminus.stubs(:compile).returns(@catalog)
- Puppet::Resource::Catalog.indirection.find(request).resource_refs.should == [ @two.ref ]
+ @one.exported = true
+ @one.virtual = true
+
+ Puppet::Resource::Catalog.indirection.find("mynode").resource_refs.should == [ @two.ref ]
end
end
diff --git a/spec/integration/indirector/direct_file_server_spec.rb b/spec/integration/indirector/direct_file_server_spec.rb
index b82734845..3a6b96250 100755
--- a/spec/integration/indirector/direct_file_server_spec.rb
+++ b/spec/integration/indirector/direct_file_server_spec.rb
@@ -15,7 +15,7 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with the files
it "should return an instance of the model" do
pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do
- FileTest.expects(:exists?).with(@filepath).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(@filepath).returns(true)
@terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}", nil)).should be_instance_of(Puppet::FileServing::Content)
end
@@ -23,11 +23,9 @@ describe Puppet::Indirector::DirectFileServer, " when interacting with the files
it "should return an instance capable of returning its content" do
pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do
- FileTest.expects(:exists?).with(@filepath).returns(true)
- File.stubs(:lstat).with(@filepath).returns(stub("stat", :ftype => "file"))
- IO.expects(:binread).with(@filepath).returns("my content")
+ filename = file_containing("testfile", "my content")
- instance = @terminus.find(@terminus.indirection.request(:find, "file://host#{@filepath}", nil))
+ instance = @terminus.find(@terminus.indirection.request(:find, "file://host#{filename}", nil))
instance.content.should == "my content"
end
diff --git a/spec/integration/indirector/file_content/file_server_spec.rb b/spec/integration/indirector/file_content/file_server_spec.rb
index 00bdda412..44e2fabee 100755
--- a/spec/integration/indirector/file_content/file_server_spec.rb
+++ b/spec/integration/indirector/file_content/file_server_spec.rb
@@ -59,8 +59,8 @@ describe Puppet::Indirector::FileContent::FileServer, " when finding files" do
end
it "should find file content in files when node name expansions are used" do
- FileTest.stubs(:exists?).returns true
- FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true)
@path = tmpfile("file_server_testing")
diff --git a/spec/integration/network/server/webrick_spec.rb b/spec/integration/network/server/webrick_spec.rb
deleted file mode 100755
index 5ae490410..000000000
--- a/spec/integration/network/server/webrick_spec.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-#! /usr/bin/env ruby
-require 'spec_helper'
-require 'puppet/network/server'
-require 'puppet/ssl/certificate_authority'
-require 'socket'
-
-describe Puppet::Network::Server, :unless => Puppet.features.microsoft_windows? do
- describe "when using webrick" do
- include PuppetSpec::Files
-
- # This reduces the odds of conflicting port numbers between concurrent runs
- # of the suite on the same machine dramatically.
- let(:port) { 20000 + ($$ % 40000) }
- let(:address) { '127.0.0.1' }
-
- before :each do
- Puppet[:server] = '127.0.0.1'
-
- # Get a safe temporary file
- dir = tmpdir("webrick_integration_testing")
-
- Puppet.settings[:confdir] = dir
- Puppet.settings[:vardir] = dir
- Puppet.settings[:logdir] = dir
- Puppet.settings[:group] = Process.gid
-
- Puppet::SSL::Host.ca_location = :local
-
- ca = Puppet::SSL::CertificateAuthority.new
- ca.generate(Puppet[:certname]) unless Puppet::SSL::Certificate.indirection.find(Puppet[:certname])
-
- @server = Puppet::Network::Server.new(address, port)
- end
-
- after do
- Puppet::SSL::Host.ca_location = :none
- end
-
- describe "before listening" do
- it "should not be reachable at the specified address and port" do
- expect { TCPSocket.new('127.0.0.1', port) }.to raise_error
- end
- end
-
- describe "when listening" do
- it "should be reachable on the specified address and port" do
- @server.start
- expect { TCPSocket.new('127.0.0.1', port) }.to_not raise_error
- end
-
- it "should use any specified bind address" do
- @server.stubs(:stop) # we're breaking listening internally, so we have to keep it from unlistening
- Puppet::Network::HTTP::WEBrick.any_instance.expects(:listen).with(address, port)
- @server.start
- end
-
- it "should not allow multiple servers to listen on the same address and port" do
- @server.start
- server2 = Puppet::Network::Server.new(address, port)
- expect { server2.start }.to raise_error
- end
-
- after :each do
- @server.stop if @server && @server.listening?
- end
- end
-
- describe "after unlistening" do
- it "should not be reachable on the port and address assigned" do
- @server.start
- @server.stop
- expect { TCPSocket.new('127.0.0.1', port) }.to raise_error(Errno::ECONNREFUSED)
- end
- end
- end
-end
diff --git a/spec/integration/node/facts_spec.rb b/spec/integration/node/facts_spec.rb
index d307be35a..fa9fd4905 100755
--- a/spec/integration/node/facts_spec.rb
+++ b/spec/integration/node/facts_spec.rb
@@ -23,7 +23,7 @@ describe Puppet::Node::Facts do
terminus = Puppet::Node::Facts.indirection.terminus(:yaml)
terminus.expects(:path).with("me").returns "/my/yaml/file"
- FileTest.expects(:exist?).with("/my/yaml/file").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("/my/yaml/file").returns false
Puppet::Node::Facts.indirection.find("me").should be_nil
end
diff --git a/spec/integration/node_spec.rb b/spec/integration/node_spec.rb
index 5ea3df9aa..ef862ef28 100755
--- a/spec/integration/node_spec.rb
+++ b/spec/integration/node_spec.rb
@@ -21,7 +21,7 @@ describe Puppet::Node do
terminus.expects(:path).with(@name).returns "/my/yaml/file"
- FileTest.expects(:exist?).with("/my/yaml/file").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("/my/yaml/file").returns false
Puppet::Node.indirection.find(@name).should be_nil
end
diff --git a/spec/integration/parser/compiler_spec.rb b/spec/integration/parser/compiler_spec.rb
index 01e5c085f..eb87d03ed 100755
--- a/spec/integration/parser/compiler_spec.rb
+++ b/spec/integration/parser/compiler_spec.rb
@@ -1,8 +1,11 @@
#! /usr/bin/env ruby
require 'spec_helper'
require 'puppet/parser/parser_factory'
+require 'puppet_spec/compiler'
describe "Puppet::Parser::Compiler" do
+ include PuppetSpec::Compiler
+
before :each do
@node = Puppet::Node.new "testnode"
@@ -310,6 +313,93 @@ describe "Puppet::Parser::Compiler" do
expected_subscriptions << ['b', 'c'] << ['e', 'd']
end
end
+
+ context 'when working with the trusted data hash' do
+ context 'and have opted in to trusted_node_data' do
+ before :each do
+ Puppet[:trusted_node_data] = true
+ end
+
+ it 'should make $trusted available' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ notify { 'test': message => $trusted[data] }
+ MANIFEST
+
+ catalog.resource("Notify[test]")[:message].should == "value"
+ end
+
+ it 'should not allow assignment to $trusted' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ $trusted = 'changed'
+ notify { 'test': message => $trusted == 'changed' }
+ MANIFEST
+ catalog.resource("Notify[test]")[:message].should == true
+ end.to raise_error(Puppet::Error, /Attempt to assign to a reserved variable name: 'trusted'/)
+ end
+
+ it 'should not allow addition to $trusted hash' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ $trusted['extra'] = 'added'
+ notify { 'test': message => $trusted['extra'] == 'added' }
+ MANIFEST
+ catalog.resource("Notify[test]")[:message].should == true
+ # different errors depending on regular or future parser
+ end.to raise_error(Puppet::Error, /(can't modify frozen [hH]ash)|(Illegal attempt to assign)/)
+ end
+
+ it 'should not allow addition to $trusted hash via Ruby inline template' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+
+ expect do
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ $dummy = inline_template("<% @trusted['extra'] = 'added' %> lol")
+ notify { 'test': message => $trusted['extra'] == 'added' }
+ MANIFEST
+ catalog.resource("Notify[test]")[:message].should == true
+ end.to raise_error(Puppet::Error, /can't modify frozen [hH]ash/)
+ end
+ end
+
+ context 'and have not opted in to trusted_node_data' do
+ before :each do
+ Puppet[:trusted_node_data] = false
+ end
+
+ it 'should not make $trusted available' do
+ node = Puppet::Node.new("testing")
+ node.trusted_data = { "data" => "value" }
+
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ notify { 'test': message => $trusted == undef }
+ MANIFEST
+
+ catalog.resource("Notify[test]")[:message].should == true
+ end
+
+ it 'should allow assignment to $trusted' do
+ node = Puppet::Node.new("testing")
+
+ catalog = compile_to_catalog(<<-MANIFEST, node)
+ $trusted = 'changed'
+ notify { 'test': message => $trusted == 'changed' }
+ MANIFEST
+
+ catalog.resource("Notify[test]")[:message].should == true
+ end
+ end
+ end
end
describe 'using classic parser' do
diff --git a/spec/integration/parser/functions_spec.rb b/spec/integration/parser/functions_spec.rb
deleted file mode 100755
index 51cfb92c1..000000000
--- a/spec/integration/parser/functions_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-#! /usr/bin/env ruby
-require 'spec_helper'
-
-describe Puppet::Parser::Functions do
- it "should support multiple threads autoloading the same function" do
- threads = []
- lambda {
- 10.times { |a|
- threads << Thread.new {
- Puppet::Parser::Functions.function("template")
- }
- }
- }.should_not raise_error
- threads.each { |t| t.join }
- end
-end
diff --git a/spec/integration/parser/parser_spec.rb b/spec/integration/parser/parser_spec.rb
index 759643a0f..280fd040f 100755
--- a/spec/integration/parser/parser_spec.rb
+++ b/spec/integration/parser/parser_spec.rb
@@ -213,11 +213,11 @@ describe "Puppet::Parser::Parser" do
end
it 'should flag illegal use of non r-value producing <| |>' do
- expect { @parser.parse("$a = file <| |>") }.to raise_error(/A Virtual Query does not produce a value at line 1:6/)
+ expect { @parser.parse("$a = File <| |>") }.to raise_error(/A Virtual Query does not produce a value at line 1:6/)
end
it 'should flag illegal use of non r-value producing <<| |>>' do
- expect { @parser.parse("$a = file <<| |>>") }.to raise_error(/An Exported Query does not produce a value at line 1:6/)
+ expect { @parser.parse("$a = File <<| |>>") }.to raise_error(/An Exported Query does not produce a value at line 1:6/)
end
it 'should flag illegal use of non r-value producing define' do
diff --git a/spec/integration/provider/cron/crontab_spec.rb b/spec/integration/provider/cron/crontab_spec.rb
index 4681ff109..3c262c926 100644
--- a/spec/integration/provider/cron/crontab_spec.rb
+++ b/spec/integration/provider/cron/crontab_spec.rb
@@ -10,12 +10,12 @@ describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless =
Puppet::Type.type(:cron).stubs(:defaultprovider).returns described_class
Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to filebucket
- # I dont want to execute anything
+ # I don't want to execute anything
described_class.stubs(:filetype).returns Puppet::Util::FileType::FileTypeFlat
described_class.stubs(:default_target).returns crontab_user1
- # I dont want to stub Time.now to get a static header because I dont know
- # where Time.now is used elsewere so just go with a very simple header
+ # I don't want to stub Time.now to get a static header because I don't know
+ # where Time.now is used elsewhere, so just go with a very simple header
described_class.stubs(:header).returns "# HEADER: some simple\n# HEADER: header\n"
FileUtils.cp(my_fixture('crontab_user1'), crontab_user1)
FileUtils.cp(my_fixture('crontab_user2'), crontab_user2)
@@ -191,8 +191,6 @@ describe Puppet::Type.type(:cron).provider(:crontab), '(integration)', :unless =
File.read(crontab_user2).should == File.read(my_fixture('moved_cronjob_input2'))
end
end
-
- it "should not add multiple headers"
end
end
diff --git a/spec/integration/resource/catalog_spec.rb b/spec/integration/resource/catalog_spec.rb
index 3dc30560c..1469cc063 100755
--- a/spec/integration/resource/catalog_spec.rb
+++ b/spec/integration/resource/catalog_spec.rb
@@ -21,7 +21,7 @@ describe Puppet::Resource::Catalog do
terminus = Puppet::Resource::Catalog.indirection.terminus(:yaml)
terminus.expects(:path).with("me").returns "/my/yaml/file"
- FileTest.expects(:exist?).with("/my/yaml/file").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("/my/yaml/file").returns false
Puppet::Resource::Catalog.indirection.find("me").should be_nil
end
diff --git a/spec/integration/ssl/autosign_spec.rb b/spec/integration/ssl/autosign_spec.rb
new file mode 100644
index 000000000..003796ef0
--- /dev/null
+++ b/spec/integration/ssl/autosign_spec.rb
@@ -0,0 +1,130 @@
+require 'spec_helper'
+
+describe "autosigning" do
+ include PuppetSpec::Files
+
+ let(:puppet_dir) { tmpdir("ca_autosigning") }
+ let(:csr_attributes_content) do
+ {
+ 'custom_attributes' => {
+ '1.3.6.1.4.1.34380.2.0' => 'hostname.domain.com',
+ '1.3.6.1.4.1.34380.2.1' => 'my passphrase',
+ '1.3.6.1.4.1.34380.2.2' => # system IPs in hex
+ [ 0xC0A80001, # 192.168.0.1
+ 0xC0A80101 ], # 192.168.1.1
+ },
+ 'extension_requests' => {
+ 'pp_uuid' => 'abcdef',
+ '1.3.6.1.4.1.34380.1.1.2' => '1234', # pp_instance_id
+ '1.3.6.1.4.1.34380.1.2.1' => 'some-value', # private extension
+ },
+ }
+ end
+
+ let(:host) { Puppet::SSL::Host.new }
+
+ before do
+ Puppet.settings[:confdir] = puppet_dir
+ Puppet.settings[:vardir] = puppet_dir
+
+ # This is necessary so the terminus instances don't lie around.
+ Puppet::SSL::Key.indirection.termini.clear
+ end
+
+ def write_csr_attributes(yaml)
+ File.open(Puppet.settings[:csr_attributes], 'w') do |file|
+ file.puts YAML.dump(yaml)
+ end
+ end
+
+ context "when the csr_attributes file is valid, but empty" do
+ it "generates a CSR when the file is empty" do
+ Puppet::FileSystem::File.new(Puppet.settings[:csr_attributes]).touch
+
+ host.generate_certificate_request
+ end
+
+ it "generates a CSR when the file contains whitespace" do
+ File.open(Puppet.settings[:csr_attributes], 'w') do |file|
+ file.puts "\n\n"
+ end
+
+ host.generate_certificate_request
+ end
+ end
+
+ context "when the csr_attributes file doesn't contain a YAML encoded hash" do
+ it "raises when the file contains a string" do
+ write_csr_attributes('a string')
+
+ expect {
+ host.generate_certificate_request
+ }.to raise_error(Puppet::Error, /invalid CSR attributes, expected instance of Hash, received instance of String/)
+ end
+
+ it "raises when the file contains an empty array" do
+ write_csr_attributes([])
+
+ expect {
+ host.generate_certificate_request
+ }.to raise_error(Puppet::Error, /invalid CSR attributes, expected instance of Hash, received instance of Array/)
+ end
+ end
+
+ context "with extension requests from csr_attributes file" do
+ let(:ca) { Puppet::SSL::CertificateAuthority.new }
+
+ it "generates a CSR when the csr_attributes file is an empty hash" do
+ write_csr_attributes(csr_attributes_content)
+
+ host.generate_certificate_request
+ end
+
+ context "and subjectAltName" do
+ it "raises an error if you include subjectAltName in csr_attributes" do
+ csr_attributes_content['extension_requests']['subjectAltName'] = 'foo'
+ write_csr_attributes(csr_attributes_content)
+ expect { host.generate_certificate_request }.to raise_error(Puppet::Error, /subjectAltName.*conflicts with internally used extension request/)
+ end
+
+ it "properly merges subjectAltName when in settings" do
+ Puppet.settings[:dns_alt_names] = 'althostname.nowhere'
+ write_csr_attributes(csr_attributes_content)
+ host.generate_certificate_request
+ csr = Puppet::SSL::CertificateRequest.indirection.find(host.name)
+ expect(csr.subject_alt_names).to include('DNS:althostname.nowhere')
+ end
+ end
+
+ context "without subjectAltName" do
+
+ before do
+ write_csr_attributes(csr_attributes_content)
+ host.generate_certificate_request
+ end
+
+ it "pulls extension attributes from the csr_attributes file into the certificate" do
+ csr = Puppet::SSL::CertificateRequest.indirection.find(host.name)
+ expect(csr.request_extensions).to have(3).items
+ expect(csr.request_extensions).to include('oid' => 'pp_uuid', 'value' => 'abcdef')
+ expect(csr.request_extensions).to include('oid' => 'pp_instance_id', 'value' => '1234')
+ expect(csr.request_extensions).to include('oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'some-value')
+ end
+
+ it "copies extension requests to certificate" do
+ cert = ca.sign(host.name)
+ expect(cert.custom_extensions).to include('oid' => 'pp_uuid', 'value' => 'abcdef')
+ expect(cert.custom_extensions).to include('oid' => 'pp_instance_id', 'value' => '1234')
+ expect(cert.custom_extensions).to include('oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'some-value')
+ end
+
+ it "does not copy custom attributes to certificate" do
+ cert = ca.sign(host.name)
+ cert.custom_extensions.each do |ext|
+ expect(Puppet::SSL::Oids.subtree_of?('1.3.6.1.4.1.34380.2', ext['oid'])).to be_false
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/integration/ssl/certificate_authority_spec.rb b/spec/integration/ssl/certificate_authority_spec.rb
index 724f6b371..acbc38cbc 100755
--- a/spec/integration/ssl/certificate_authority_spec.rb
+++ b/spec/integration/ssl/certificate_authority_spec.rb
@@ -6,8 +6,9 @@ require 'puppet/ssl/certificate_authority'
describe Puppet::SSL::CertificateAuthority, :unless => Puppet.features.microsoft_windows? do
include PuppetSpec::Files
+ let(:ca) { @ca }
+
before do
- # Get a safe temporary file
dir = tmpdir("ca_integration_testing")
Puppet.settings[:confdir] = dir
@@ -15,103 +16,58 @@ describe Puppet::SSL::CertificateAuthority, :unless => Puppet.features.microsoft
Puppet.settings[:group] = Process.gid
Puppet::SSL::Host.ca_location = :local
- @ca = Puppet::SSL::CertificateAuthority.new
- end
-
- after {
- Puppet::SSL::Host.ca_location = :none
-
- Puppet.settings.clear
-
- 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)
+ # this has the side-effect of creating the various directories that we need
+ @ca = Puppet::SSL::CertificateAuthority.new
end
it "should be able to generate a new host certificate" do
- @ca.generate("newhost")
+ 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.generate("newhost")
- @ca.revoke("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
+ expect { ca.verify("newhost") }.to raise_error(Puppet::SSL::CertificateAuthority::CertificateVerificationError, "certificate revoked")
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")
+ host = certificate_request_for("luke.madstop.com")
+
+ 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
+ host = certificate_request_for("luke.madstop.com")
+ other = certificate_request_for("other.madstop.com")
- @ca.sign("luke.madstop.com")
- @ca.sign("other.madstop.com")
+ 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")
+ host = certificate_request_for("luke.madstop.com")
+
+ 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")
+ host = certificate_request_for("luke.madstop.com")
+
+ ca.sign("luke.madstop.com")
unless ssl = Puppet::Util::which('openssl')
pending "No ssl available"
@@ -124,21 +80,58 @@ describe Puppet::SSL::CertificateAuthority, :unless => Puppet.features.microsoft
end
it "should verify proof of possession when signing certificates" do
- csr = @host.certificate_request
- wrong_key = Puppet::SSL::Key.new(@host.name)
+ host = certificate_request_for("luke.madstop.com")
+ csr = host.certificate_request
+ wrong_key = Puppet::SSL::Key.new(host.name)
wrong_key.generate
csr.content.public_key = wrong_key.content.public_key
# The correct key has to be removed so we can save the incorrect one
- Puppet::SSL::CertificateRequest.indirection.destroy(@host.name)
+ Puppet::SSL::CertificateRequest.indirection.destroy(host.name)
Puppet::SSL::CertificateRequest.indirection.save(csr)
expect {
- @ca.sign(@host.name)
+ ca.sign(host.name)
}.to raise_error(
Puppet::SSL::CertificateAuthority::CertificateSigningError,
"CSR contains a public key that does not correspond to the signing key"
)
end
end
+
+ it "allows autosigning certificates concurrently", :unless => Puppet::Util::Platform.windows? do
+ Puppet[:autosign] = true
+ hosts = (0..4).collect { |i| certificate_request_for("host#{i}") }
+
+ run_in_parallel(5) do |i|
+ ca.autosign(Puppet::SSL::CertificateRequest.indirection.find(hosts[i].name))
+ end
+
+ certs = hosts.collect { |host| Puppet::SSL::Certificate.indirection.find(host.name).content }
+ serial_numbers = certs.collect(&:serial)
+
+ serial_numbers.sort.should == [2, 3, 4, 5, 6] # serial 1 is the ca certificate
+ end
+
+ def certificate_request_for(hostname)
+ key = Puppet::SSL::Key.new(hostname)
+ key.generate
+
+ host = Puppet::SSL::Host.new(hostname)
+ host.key = key
+ host.generate_certificate_request
+
+ host
+ end
+
+ def run_in_parallel(number)
+ children = []
+ number.times do |i|
+ children << Kernel.fork do
+ yield i
+ end
+ end
+
+ children.each { |pid| Process.wait(pid) }
+ end
end
diff --git a/spec/integration/ssl/certificate_revocation_list_spec.rb b/spec/integration/ssl/certificate_revocation_list_spec.rb
index add481151..530f03ed9 100755
--- a/spec/integration/ssl/certificate_revocation_list_spec.rb
+++ b/spec/integration/ssl/certificate_revocation_list_spec.rb
@@ -29,7 +29,7 @@ describe Puppet::SSL::CertificateRevocationList do
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])
+ raise "CRL not created" unless Puppet::FileSystem::File.exist?(Puppet[:hostcrl])
crl = Puppet::SSL::CertificateRevocationList.new("crl_int_testing")
crl.read(Puppet[:hostcrl])
diff --git a/spec/integration/ssl/host_spec.rb b/spec/integration/ssl/host_spec.rb
index d8191f9b4..fb6b4e1e0 100755
--- a/spec/integration/ssl/host_spec.rb
+++ b/spec/integration/ssl/host_spec.rb
@@ -70,7 +70,7 @@ describe Puppet::SSL::Host do
@ca = Puppet::SSL::Host.new(Puppet::SSL::Host.ca_name)
@ca.generate_key
- FileTest.should_not be_exist(File.join(Puppet[:privatekeydir], "ca.pem"))
+ Puppet::FileSystem::File.exist?(File.join(Puppet[:privatekeydir], "ca.pem")).should be_false
end
end
diff --git a/spec/integration/transaction_spec.rb b/spec/integration/transaction_spec.rb
index 4fdd69315..851954dbe 100755
--- a/spec/integration/transaction_spec.rb
+++ b/spec/integration/transaction_spec.rb
@@ -64,7 +64,7 @@ describe Puppet::Transaction do
catalog.add_resource resource
catalog.apply
- FileTest.should be_exist(path)
+ Puppet::FileSystem::File.exist?(path).should be_true
end
it "should not apply virtual exported resources" do
@@ -189,8 +189,8 @@ describe Puppet::Transaction do
catalog = mk_catalog(file, exec1, exec2)
catalog.apply
- FileTest.should be_exist(file1)
- FileTest.should be_exist(file2)
+ Puppet::FileSystem::File.exist?(file1).should be_true
+ Puppet::FileSystem::File.exist?(file2).should be_true
end
it "should not let one failed refresh result in other refreshes failing" do
@@ -223,7 +223,7 @@ describe Puppet::Transaction do
catalog = mk_catalog(file, exec1, exec2)
catalog.apply
- FileTest.should be_exists(newfile)
+ Puppet::FileSystem::File.exist?(newfile).should be_true
end
it "should still trigger skipped resources" do
@@ -251,18 +251,18 @@ describe Puppet::Transaction do
# Run it once
catalog.apply
- FileTest.should be_exists(fname)
+ Puppet::FileSystem::File.exist?(fname).should be_true
# Now remove it, so it can get created again
- File.unlink(fname)
+ Puppet::FileSystem::File.unlink(fname)
file[:content] = "some content"
catalog.apply
- FileTest.should be_exists(fname)
+ Puppet::FileSystem::File.exist?(fname).should be_true
# Now remove it, so it can get created again
- File.unlink(fname)
+ Puppet::FileSystem::File.unlink(fname)
# And tag our exec
exec.tag("testrun")
@@ -275,7 +275,7 @@ describe Puppet::Transaction do
file[:content] = "totally different content"
catalog.apply
- FileTest.should be_exists(fname)
+ Puppet::FileSystem::File.exist?(fname).should be_true
end
it "should not attempt to evaluate resources with failed dependencies" do
@@ -302,8 +302,8 @@ describe Puppet::Transaction do
catalog = mk_catalog(exec, file1, file2)
catalog.apply
- FileTest.should_not be_exists(file1[:path])
- FileTest.should_not be_exists(file2[:path])
+ Puppet::FileSystem::File.exist?(file1[:path]).should be_false
+ Puppet::FileSystem::File.exist?(file2[:path]).should be_false
end
it "should not trigger subscribing resources on failure" do
@@ -328,8 +328,8 @@ describe Puppet::Transaction do
catalog = mk_catalog(exec, create_file1, create_file2)
catalog.apply
- FileTest.should_not be_exists(file1)
- FileTest.should_not be_exists(file2)
+ Puppet::FileSystem::File.exist?(file1).should be_false
+ Puppet::FileSystem::File.exist?(file2).should be_false
end
# #801 -- resources only checked in noop should be rescheduled immediately.
diff --git a/spec/integration/type/exec_spec.rb b/spec/integration/type/exec_spec.rb
index d31c20134..2b044473f 100755
--- a/spec/integration/type/exec_spec.rb
+++ b/spec/integration/type/exec_spec.rb
@@ -33,7 +33,7 @@ describe Puppet::Type.type(:exec) do
catalog.add_resource exec
catalog.apply
- File.should_not be_exist(path)
+ Puppet::FileSystem::File.exist?(path).should be_false
end
it "should execute the command if onlyif returns zero" do
@@ -72,6 +72,6 @@ describe Puppet::Type.type(:exec) do
catalog.add_resource exec
catalog.apply
- File.should_not be_exist(path)
+ Puppet::FileSystem::File.exist?(path).should be_false
end
end
diff --git a/spec/integration/type/file_spec.rb b/spec/integration/type/file_spec.rb
index 5377251fd..7cd39aace 100755
--- a/spec/integration/type/file_spec.rb
+++ b/spec/integration/type/file_spec.rb
@@ -21,21 +21,28 @@ describe Puppet::Type.type(:file) do
File.join(parent, 'file_testing')
end
+ let(:dir) do
+ # we create a directory first so backups of :path that are stored in
+ # the same directory will also be removed after the tests
+ parent = tmpdir('file_spec')
+ File.join(parent, 'dir_testing')
+ end
+
if Puppet.features.posix?
def set_mode(mode, file)
File.chmod(mode, file)
end
def get_mode(file)
- File.lstat(file).mode
+ Puppet::FileSystem::File.new(file).lstat.mode
end
def get_owner(file)
- File.lstat(file).uid
+ Puppet::FileSystem::File.new(file).lstat.uid
end
def get_group(file)
- File.lstat(file).gid
+ Puppet::FileSystem::File.new(file).lstat.gid
end
else
class SecurityHelper
@@ -57,6 +64,10 @@ describe Puppet::Type.type(:file) do
def get_group(file)
SecurityHelper.get_group(file)
end
+
+ def get_aces_for_path_by_sid(path, sid)
+ SecurityHelper.get_aces_for_path_by_sid(path, sid)
+ end
end
before do
@@ -72,7 +83,7 @@ describe Puppet::Type.type(:file) do
status = catalog.apply.report.resource_statuses["File[#{source}]"]
status.should_not be_failed
status.should_not be_changed
- File.should_not be_exist(source)
+ Puppet::FileSystem::File.exist?(source).should be_false
end
describe "when ensure is absent" do
@@ -81,14 +92,14 @@ describe Puppet::Type.type(:file) do
catalog.add_resource(described_class.new(:path => path, :ensure => :absent, :backup => :false))
report = catalog.apply.report
report.resource_statuses["File[#{path}]"].should_not be_failed
- File.should_not be_exist(path)
+ Puppet::FileSystem::File.exist?(path).should be_false
end
it "should do nothing if file is not present" do
catalog.add_resource(described_class.new(:path => path, :ensure => :absent, :backup => :false))
report = catalog.apply.report
report.resource_statuses["File[#{path}]"].should_not be_failed
- File.should_not be_exist(path)
+ Puppet::FileSystem::File.exist?(path).should be_false
end
# issue #14599
@@ -204,7 +215,7 @@ describe Puppet::Type.type(:file) do
end
end
- describe "for links", :unless => Puppet.features.microsoft_windows? do
+ describe "for links", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
let(:link) { tmpfile('link_mode') }
describe "when managing links" do
@@ -214,7 +225,7 @@ describe Puppet::Type.type(:file) do
FileUtils.touch(link_target)
File.chmod(0444, link_target)
- File.symlink(link_target, link)
+ Puppet::FileSystem::File.new(link_target).symlink(link)
end
it "should not set the executable bit on the link nor the target" do
@@ -222,8 +233,8 @@ describe Puppet::Type.type(:file) do
catalog.apply
- (File.stat(link).mode & 07777) == 0666
- (File.lstat(link_target).mode & 07777) == 0444
+ (Puppet::FileSystem::File.new(link).stat.mode & 07777) == 0666
+ (Puppet::FileSystem::File.new(link_target).lstat.mode & 07777) == 0444
end
it "should ignore dangling symlinks (#6856)" do
@@ -232,7 +243,7 @@ describe Puppet::Type.type(:file) do
catalog.add_resource described_class.new(:path => link, :ensure => :link, :mode => 0666, :target => link_target, :links => :manage)
catalog.apply
- File.should_not be_exist(link)
+ Puppet::FileSystem::File.exist?(link).should be_false
end
it "should create a link to the target if ensure is omitted" do
@@ -240,9 +251,9 @@ describe Puppet::Type.type(:file) do
catalog.add_resource described_class.new(:path => link, :target => link_target)
catalog.apply
- File.should be_exist link
- File.lstat(link).ftype.should == 'link'
- File.readlink(link).should == link_target
+ Puppet::FileSystem::File.exist?(link).should be_true
+ Puppet::FileSystem::File.new(link).lstat.ftype.should == 'link'
+ Puppet::FileSystem::File.new(link).readlink().should == link_target
end
end
@@ -251,7 +262,7 @@ describe Puppet::Type.type(:file) do
target = tmpfile('dangling')
FileUtils.touch(target)
- File.symlink(target, link)
+ Puppet::FileSystem::File.new(target).symlink(link)
File.delete(target)
catalog.add_resource described_class.new(:path => path, :source => link, :mode => 0600, :links => :follow)
@@ -264,7 +275,7 @@ describe Puppet::Type.type(:file) do
before :each do
File.chmod(0600, link_target)
- File.symlink(link_target, link)
+ Puppet::FileSystem::File.new(link_target).symlink(link)
end
after :each do
@@ -327,7 +338,7 @@ describe Puppet::Type.type(:file) do
before :each do
FileUtils.touch(link_target)
- File.symlink(link_target, link)
+ Puppet::FileSystem::File.new(link_target).symlink(link)
end
it "should create the file, not a symlink (#2817, #10315)" do
@@ -357,8 +368,8 @@ describe Puppet::Type.type(:file) do
File.chmod(0666, real_target)
# link -> target -> real_target
- File.symlink(real_target, target)
- File.symlink(target, link)
+ Puppet::FileSystem::File.new(real_target).symlink(target)
+ Puppet::FileSystem::File.new(target).symlink(link)
end
after :each do
@@ -397,13 +408,13 @@ describe Puppet::Type.type(:file) do
catalog.add_resource file
catalog.add_resource filebucket
- File.open(file[:path], "wb") { |f| f.puts "bar" }
+ File.open(file[:path], "w") { |f| f.write("bar") }
- md5 = Digest::MD5.hexdigest(IO.binread(file[:path]))
+ md5 = Digest::MD5.hexdigest("bar")
catalog.apply
- filebucket.bucket.getfile(md5).should == "bar\n"
+ filebucket.bucket.getfile(md5).should == "bar"
end
it "should backup files in the local directory when a backup string is provided" do
@@ -415,7 +426,7 @@ describe Puppet::Type.type(:file) do
catalog.apply
backup = file[:path] + ".bak"
- FileTest.should be_exist(backup)
+ Puppet::FileSystem::File.exist?(backup).should be_true
File.read(backup).should == "bar\n"
end
@@ -437,7 +448,7 @@ describe Puppet::Type.type(:file) do
File.read(file[:path]).should == "bar\n"
end
- it "should not backup symlinks", :unless => Puppet.features.microsoft_windows? do
+ it "should not backup symlinks", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
link = tmpfile("link")
dest1 = tmpfile("dest1")
dest2 = tmpfile("dest2")
@@ -447,14 +458,14 @@ describe Puppet::Type.type(:file) do
catalog.add_resource bucket
File.open(dest1, "w") { |f| f.puts "whatever" }
- File.symlink(dest1, link)
+ Puppet::FileSystem::File.new(dest1).symlink(link)
md5 = Digest::MD5.hexdigest(File.read(file[:path]))
catalog.apply
- File.readlink(link).should == dest2
- File.exist?(bucket[:path]).should be_false
+ Puppet::FileSystem::File.new(link).readlink().should == dest2
+ Puppet::FileSystem::File.exist?(bucket[:path]).should be_false
end
it "should backup directories to the local filesystem by copying the whole directory" do
@@ -574,7 +585,7 @@ describe Puppet::Type.type(:file) do
end
end
- it "should be able to recursively make links to other files", :unless => Puppet.features.microsoft_windows? do
+ it "should be able to recursively make links to other files", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
source = tmpfile("file_link_integration_source")
build_path(source)
@@ -590,13 +601,13 @@ describe Puppet::Type.type(:file) do
@dirs.each do |path|
link_path = path.sub(source, dest)
- File.lstat(link_path).should be_directory
+ Puppet::FileSystem::File.new(link_path).lstat.should be_directory
end
@files.each do |path|
link_path = path.sub(source, dest)
- File.lstat(link_path).ftype.should == "link"
+ Puppet::FileSystem::File.new(link_path).lstat.ftype.should == "link"
end
end
@@ -616,13 +627,13 @@ describe Puppet::Type.type(:file) do
@dirs.each do |path|
newpath = path.sub(source, dest)
- File.lstat(newpath).should be_directory
+ Puppet::FileSystem::File.new(newpath).lstat.should be_directory
end
@files.each do |path|
newpath = path.sub(source, dest)
- File.lstat(newpath).ftype.should == "file"
+ Puppet::FileSystem::File.new(newpath).lstat.ftype.should == "file"
end
end
@@ -685,8 +696,8 @@ describe Puppet::Type.type(:file) do
catalog.apply
File.should be_directory(path)
- File.should_not be_exist(File.join(path, 'one'))
- File.should be_exist(File.join(path, 'three', 'four'))
+ Puppet::FileSystem::File.exist?(File.join(path, 'one')).should be_false
+ Puppet::FileSystem::File.exist?(File.join(path, 'three', 'four')).should be_true
end
it "should recursively copy an empty directory" do
@@ -707,7 +718,7 @@ describe Puppet::Type.type(:file) do
catalog.apply
File.should be_directory(path)
- File.should_not be_exist(File.join(path, 'a'))
+ Puppet::FileSystem::File.exist?(File.join(path, 'a')).should be_false
end
it "should only recurse one level" do
@@ -731,9 +742,9 @@ describe Puppet::Type.type(:file) do
catalog.apply
- File.should be_exist(File.join(path, 'a'))
- File.should_not be_exist(File.join(path, 'a', 'b'))
- File.should_not be_exist(File.join(path, 'z'))
+ Puppet::FileSystem::File.exist?(File.join(path, 'a')).should be_true
+ Puppet::FileSystem::File.exist?(File.join(path, 'a', 'b')).should be_false
+ Puppet::FileSystem::File.exist?(File.join(path, 'z')).should be_false
end
end
@@ -830,10 +841,10 @@ describe Puppet::Type.type(:file) do
catalog.add_resource obj
catalog.apply
- File.should be_exist(File.join(path, 'a'))
- File.should_not be_exist(File.join(path, 'a', 'b'))
- File.should be_exist(File.join(path, 'z'))
- File.should_not be_exist(File.join(path, 'z', 'y'))
+ Puppet::FileSystem::File.exist?(File.join(path, 'a')).should be_true
+ Puppet::FileSystem::File.exist?(File.join(path, 'a', 'b')).should be_false
+ Puppet::FileSystem::File.exist?(File.join(path, 'z')).should be_true
+ Puppet::FileSystem::File.exist?(File.join(path, 'z', 'y')).should be_false
end
end
end
@@ -897,7 +908,7 @@ describe Puppet::Type.type(:file) do
expected_mode = Puppet.features.microsoft_windows? ? 0644 : 0755
File.read(dest).should == "foo"
- (File.stat(dest).mode & 007777).should == expected_mode
+ (Puppet::FileSystem::File.new(dest).stat.mode & 007777).should == expected_mode
end
it "should be able to copy individual files even if recurse has been specified" do
@@ -949,7 +960,7 @@ describe Puppet::Type.type(:file) do
catalog.add_resource file
catalog.apply
- File.should_not be_exist(dest)
+ Puppet::FileSystem::File.exist?(dest).should be_false
end
describe "when sourcing" do
@@ -991,6 +1002,38 @@ describe Puppet::Type.type(:file) do
end
describe "on Windows systems", :if => Puppet.features.microsoft_windows? do
+ def expects_sid_granted_full_access_explicitly(path, sid)
+ inherited_ace = Windows::Security::INHERITED_ACE
+
+ aces = get_aces_for_path_by_sid(path, sid)
+ aces.should_not be_empty
+
+ aces.each do |ace|
+ ace.mask.should == Windows::File::FILE_ALL_ACCESS
+ (ace.flags & inherited_ace).should_not == inherited_ace
+ end
+ end
+
+ def expects_system_granted_full_access_explicitly(path)
+ expects_sid_granted_full_access_explicitly(path, @sids[:system])
+ end
+
+ def expects_at_least_one_inherited_ace_grants_full_access(path, sid)
+ inherited_ace = Windows::Security::INHERITED_ACE
+
+ aces = get_aces_for_path_by_sid(path, sid)
+ aces.should_not be_empty
+
+ aces.any? do |ace|
+ ace.mask == Windows::File::FILE_ALL_ACCESS &&
+ (ace.flags & inherited_ace) == inherited_ace
+ end.should be_true
+ end
+
+ def expects_at_least_one_inherited_system_ace_grants_full_access(path)
+ expects_at_least_one_inherited_ace_grants_full_access(path, @sids[:system])
+ end
+
it "should provide valid default values when ACLs are not supported" do
Puppet::Util::Windows::Security.stubs(:supports_acl?).with(source).returns false
@@ -1008,6 +1051,205 @@ describe Puppet::Type.type(:file) do
get_group(path).should =~ /^S\-1\-0\-0.*$/
get_mode(path).should == 0644
end
+
+ describe "when processing SYSTEM ACEs" do
+ before do
+ @sids = {
+ :current_user => Puppet::Util::Windows::Security.name_to_sid(Sys::Admin.get_login),
+ :system => Win32::Security::SID::LocalSystem,
+ :admin => Puppet::Util::Windows::Security.name_to_sid("Administrator"),
+ :guest => Puppet::Util::Windows::Security.name_to_sid("Guest"),
+ :users => Win32::Security::SID::BuiltinUsers,
+ :power_users => Win32::Security::SID::PowerUsers,
+ :none => Win32::Security::SID::Nobody
+ }
+ end
+
+ describe "on files" do
+ before :each do
+ @file = described_class.new(
+ :path => path,
+ :ensure => :file,
+ :source => source,
+ :backup => false
+ )
+ catalog.add_resource @file
+ end
+
+ describe "when source permissions are ignored" do
+ before :each do
+ @file[:source_permissions] = :ignore
+ end
+
+ it "preserves the inherited SYSTEM ACE" do
+ catalog.apply
+
+ expects_at_least_one_inherited_system_ace_grants_full_access(path)
+ end
+ end
+
+ describe "when permissions are insync?" do
+ it "preserves the explicit SYSTEM ACE" do
+ FileUtils.touch(path)
+
+ sd = Puppet::Util::Windows::Security.get_security_descriptor(path)
+ sd.protect = true
+ sd.owner = @sids[:none]
+ sd.group = @sids[:none]
+ Puppet::Util::Windows::Security.set_security_descriptor(source, sd)
+ Puppet::Util::Windows::Security.set_security_descriptor(path, sd)
+
+ catalog.apply
+
+ expects_system_granted_full_access_explicitly(path)
+ end
+ end
+
+ describe "when permissions are not insync?" do
+ before :each do
+ @file[:owner] = 'None'
+ @file[:group] = 'None'
+ end
+
+ it "replaces inherited SYSTEM ACEs with an uninherited one for an existing file" do
+ FileUtils.touch(path)
+
+ expects_at_least_one_inherited_system_ace_grants_full_access(path)
+
+ catalog.apply
+
+ expects_system_granted_full_access_explicitly(path)
+ end
+
+ it "replaces inherited SYSTEM ACEs for a new file with an uninherited one" do
+ catalog.apply
+
+ expects_system_granted_full_access_explicitly(path)
+ end
+ end
+
+ describe "created with SYSTEM as the group" do
+ before :each do
+ @file[:owner] = @sids[:users]
+ @file[:group] = @sids[:system]
+ @file[:mode] = 0644
+
+ catalog.apply
+ end
+
+ it "should allow the user to explicitly set the mode to 4" do
+ system_aces = get_aces_for_path_by_sid(path, @sids[:system])
+ system_aces.should_not be_empty
+
+ system_aces.each do |ace|
+ ace.mask.should == Windows::File::FILE_GENERIC_READ
+ end
+ end
+
+ it "prepends SYSTEM ace when changing group from system to power users" do
+ @file[:group] = @sids[:power_users]
+ catalog.apply
+
+ system_aces = get_aces_for_path_by_sid(path, @sids[:system])
+ system_aces.size.should == 1
+ end
+ end
+ end
+
+ describe "on directories" do
+ before :each do
+ @directory = described_class.new(
+ :path => dir,
+ :ensure => :directory
+ )
+ catalog.add_resource @directory
+ end
+
+ describe "when source permissions are ignored" do
+ before :each do
+ @directory[:source_permissions] = :ignore
+ end
+
+ it "preserves the inherited SYSTEM ACE" do
+ catalog.apply
+
+ expects_at_least_one_inherited_system_ace_grants_full_access(dir)
+ end
+ end
+
+ describe "when permissions are insync?" do
+ it "preserves the explicit SYSTEM ACE" do
+ Dir.mkdir(dir)
+
+ source_dir = tmpdir('source_dir')
+ @directory[:source] = source_dir
+
+ sd = Puppet::Util::Windows::Security.get_security_descriptor(source_dir)
+ sd.protect = true
+ sd.owner = @sids[:none]
+ sd.group = @sids[:none]
+ Puppet::Util::Windows::Security.set_security_descriptor(source_dir, sd)
+ Puppet::Util::Windows::Security.set_security_descriptor(dir, sd)
+
+ catalog.apply
+
+ expects_system_granted_full_access_explicitly(dir)
+ end
+ end
+
+ describe "when permissions are not insync?" do
+ before :each do
+ @directory[:owner] = 'None'
+ @directory[:group] = 'None'
+ @directory[:mode] = 0444
+ end
+
+ it "replaces inherited SYSTEM ACEs with an uninherited one for an existing directory" do
+ FileUtils.mkdir(dir)
+
+ expects_at_least_one_inherited_system_ace_grants_full_access(dir)
+
+ catalog.apply
+
+ expects_system_granted_full_access_explicitly(dir)
+ end
+
+ it "replaces inherited SYSTEM ACEs with an uninherited one for an existing directory" do
+ catalog.apply
+
+ expects_system_granted_full_access_explicitly(dir)
+ end
+
+ describe "created with SYSTEM as the group" do
+ before :each do
+ @directory[:owner] = @sids[:users]
+ @directory[:group] = @sids[:system]
+ @directory[:mode] = 0644
+
+ catalog.apply
+ end
+
+ it "should allow the user to explicitly set the mode to 4" do
+ system_aces = get_aces_for_path_by_sid(dir, @sids[:system])
+ system_aces.should_not be_empty
+
+ system_aces.each do |ace|
+ # unlike files, Puppet sets execute bit on directories that are readable
+ ace.mask.should == Windows::File::FILE_GENERIC_READ | Windows::File::FILE_GENERIC_EXECUTE
+ end
+ end
+
+ it "prepends SYSTEM ace when changing group from system to power users" do
+ @directory[:group] = @sids[:power_users]
+ catalog.apply
+
+ system_aces = get_aces_for_path_by_sid(dir, @sids[:system])
+ system_aces.size.should == 1
+ end
+ end
+ end
+ end
+ end
end
end
@@ -1056,7 +1298,7 @@ describe Puppet::Type.type(:file) do
end
it "should purge files that are neither remote nor otherwise managed" do
- FileTest.should_not be_exist(@purgee)
+ Puppet::FileSystem::File.exist?(@purgee).should be_false
end
end
diff --git a/spec/integration/type/tidy_spec.rb b/spec/integration/type/tidy_spec.rb
index 54458c651..562ae17e3 100755
--- a/spec/integration/type/tidy_spec.rb
+++ b/spec/integration/type/tidy_spec.rb
@@ -12,12 +12,12 @@ describe Puppet::Type.type(:tidy) do
end
# Testing #355.
- it "should be able to remove dead links", :unless => Puppet.features.microsoft_windows? do
+ it "should be able to remove dead links", :if => Puppet.features.manages_symlinks? 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)
+ Puppet::FileSystem::File.new(target).symlink(link)
tidy = Puppet::Type.type(:tidy).new :path => dir, :recurse => true
@@ -26,6 +26,6 @@ describe Puppet::Type.type(:tidy) do
catalog.apply
- FileTest.should_not be_symlink(link)
+ Puppet::FileSystem::File.new(link).symlink?.should be_false
end
end
diff --git a/spec/integration/util/rdoc/parser_spec.rb b/spec/integration/util/rdoc/parser_spec.rb
index 0a473d042..58c0a882d 100755
--- a/spec/integration/util/rdoc/parser_spec.rb
+++ b/spec/integration/util/rdoc/parser_spec.rb
@@ -1,60 +1,261 @@
#! /usr/bin/env ruby
require 'spec_helper'
+require 'puppet/util/rdoc'
-describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
+describe "RDoc::Parser" do
require 'puppet_spec/files'
include PuppetSpec::Files
- before :all do
- require 'puppet/resource/type_collection'
- require 'puppet/util/rdoc/parser'
- require 'puppet/util/rdoc'
- require 'puppet/util/rdoc/code_objects'
- require 'rdoc/options'
- require 'rdoc/rdoc'
+ let(:document_all) { false }
+ let(:tmp_dir) { tmpdir('rdoc_parser_tmp') }
+ let(:doc_dir) { File.join(tmp_dir, 'doc') }
+ let(:manifests_dir) { File.join(tmp_dir, 'manifests') }
+ let(:modules_dir) { File.join(tmp_dir, 'modules') }
+
+ let(:modules_and_manifests) do
+ {
+ :site => [
+ File.join(manifests_dir, 'site.pp'),
+ <<-EOF
+# The test class comment
+class test {
+ # The virtual resource comment
+ @notify { virtual: }
+ # The a_notify_resource comment
+ notify { a_notify_resource:
+ message => "a_notify_resource message"
+ }
+}
+
+# The includes_another class comment
+class includes_another {
+ include another
+}
+
+# The requires_another class comment
+class requires_another {
+ require another
+}
+
+# node comment
+node foo {
+ include test
+ $a_var = "var_value"
+ realize Notify[virtual]
+ notify { bar: }
+}
+ EOF
+ ],
+ :module_readme => [
+ File.join(modules_dir, 'a_module', 'README'),
+ <<-EOF
+The a_module README docs.
+ EOF
+ ],
+ :module_init => [
+ File.join(modules_dir, 'a_module', 'manifests', 'init.pp'),
+ <<-EOF
+# The a_module class comment
+class a_module {}
+
+class another {}
+ EOF
+ ],
+ :module_type => [
+ File.join(modules_dir, 'a_module', 'manifests', 'a_type.pp'),
+ <<-EOF
+# The a_type type comment
+define a_module::a_type() {}
+ EOF
+ ],
+ :module_plugin => [
+ File.join(modules_dir, 'a_module', 'lib', 'puppet', 'type', 'a_plugin.rb'),
+ <<-EOF
+# The a_plugin type comment
+Puppet::Type.newtype(:a_plugin) do
+ @doc = "Not presented"
+end
+ EOF
+ ],
+ :module_function => [
+ File.join(modules_dir, 'a_module', 'lib', 'puppet', 'parser', 'a_function.rb'),
+ <<-EOF
+# The a_function function comment
+module Puppet::Parser::Functions
+ newfunction(:a_function, :type => :rvalue) do
+ return
+ end
+end
+ EOF
+ ],
+ :module_fact => [
+ File.join(modules_dir, 'a_module', 'lib', 'facter', 'a_fact.rb'),
+ <<-EOF
+# The a_fact fact comment
+Facter.add("a_fact") do
+end
+ EOF
+ ],
+ }
end
- before :each do
- tmpdir = tmpfile('rdoc_parser_tmp')
- Dir.mkdir(tmpdir)
- @parsedfile = File.join(tmpdir, 'init.pp')
+ def write_file(file, content)
+ FileUtils.mkdir_p(File.dirname(file))
+ File.open(file, 'w') do |f|
+ f.puts(content)
+ end
+ end
- File.open(@parsedfile, 'w') do |f|
- f.puts '# comment'
- f.puts 'class ::test {}'
+ def prepare_manifests_and_modules
+ modules_and_manifests.each do |key,array|
+ write_file(*array)
end
+ end
+
+ def file_exists_and_matches_content(file, *content_patterns)
+ Puppet::FileSystem::File.exist?(file).should(be_true, "Cannot find #{file}")
+ content_patterns.each do |pattern|
+ content = File.read(file)
+ content.should match(pattern)
+ end
+ end
- @top_level = stub_everything 'toplevel', :file_relative_name => @parsedfile
- @module = stub_everything 'module'
- @puppet_top_level = RDoc::PuppetTopLevel.new(@top_level)
- RDoc::PuppetTopLevel.stubs(:new).returns(@puppet_top_level)
- @puppet_top_level.expects(:add_module).returns(@module)
- @parser = RDoc::Parser.new(@top_level, @parsedfile, nil, Options.instance, RDoc::Stats.new)
+ def some_file_exists_with_matching_content(glob, *content_patterns)
+ Dir.glob(glob).select do |f|
+ contents = File.read(f)
+ content_patterns.all? { |p| p.match(contents) }
+ end.should_not(be_empty, "Could not match #{content_patterns} in any of the files found in #{glob}")
end
- after(:each) do
- File.unlink(@parsedfile)
+ before :each do
+ prepare_manifests_and_modules
+ Puppet.settings[:document_all] = document_all
+ Puppet.settings[:modulepath] = modules_dir
+ Puppet::Util::RDoc.rdoc(doc_dir, [modules_dir, manifests_dir])
end
- def get_test_class(toplevel)
- # toplevel -> main -> test
- toplevel.classes[0].classes[0]
+ module RdocTesters
+ def has_module_rdoc(module_name, *other_test_patterns)
+ file_exists_and_matches_content(module_path(module_name), /Module:? +#{module_name}/i, *other_test_patterns)
+ end
+
+ def has_node_rdoc(module_name, node_name, *other_test_patterns)
+ file_exists_and_matches_content(node_path(module_name, node_name), /#{node_name}/, /node comment/, *other_test_patterns)
+ end
+
+ def has_defined_type(module_name, type_name)
+ file_exists_and_matches_content(module_path(module_name), /#{type_name}.*?\(\s*\)/m, "The .*?#{type_name}.*? type comment")
+ end
+
+ def has_class_rdoc(module_name, class_name, *other_test_patterns)
+ file_exists_and_matches_content(class_path(module_name, class_name), /#{class_name}.*? class comment/, *other_test_patterns)
+ end
+
+ def has_plugin_rdoc(module_name, type, name)
+ file_exists_and_matches_content(plugin_path(module_name, type, name), /The .*?#{name}.*?\s*#{type} comment/m, /Type.*?#{type}/m)
+ end
end
- it "should parse to RDoc data structure" do
- @parser.expects(:document_class).with { |n,k,c| n == "::test" and k.is_a?(Puppet::Resource::Type) }
- @parser.scan
+ shared_examples_for :an_rdoc_site do
+ it "documents the __site__ module" do
+ has_module_rdoc("__site__")
+ end
+
+ it "documents the __site__::test class" do
+ has_class_rdoc("__site__", "test")
+ end
+
+ it "documents the __site__::foo node" do
+ has_node_rdoc("__site__", "foo")
+ end
+
+ it "documents the a_module module" do
+ has_module_rdoc("a_module", /The .*?a_module.*? .*?README.*?docs/m)
+ end
+
+ it "documents the a_module::a_module class" do
+ has_class_rdoc("a_module", "a_module")
+ end
+
+ it "documents the a_module::a_type defined type" do
+ has_defined_type("a_module", "a_type")
+ end
+
+ it "documents the a_module::a_plugin type" do
+ has_plugin_rdoc("a_module", :type, 'a_plugin')
+ end
+
+ it "documents the a_module::a_function function" do
+ has_plugin_rdoc("a_module", :function, 'a_function')
+ end
+
+ it "documents the a_module::a_fact fact" do
+ has_plugin_rdoc("a_module", :fact, 'a_fact')
+ end
+
+ it "documents included classes" do
+ has_class_rdoc("__site__", "includes_another", /Included.*?another/m)
+ end
end
- it "should get a PuppetClass for the main class" do
- @parser.scan.classes[0].should be_a(RDoc::PuppetClass)
+ shared_examples_for :an_rdoc1_site do
+ it "documents required classes" do
+ has_class_rdoc("__site__", "requires_another", /Required Classes.*?another/m)
+ end
+
+ it "documents realized resources" do
+ has_node_rdoc("__site__", "foo", /Realized Resources.*?Notify\[virtual\]/m)
+ end
+
+ it "documents global variables" do
+ has_node_rdoc("__site__", "foo", /Global Variables.*?a_var.*?=.*?var_value/m)
+ end
+
+ describe "when document_all is true" do
+ let(:document_all) { true }
+
+ it "documents virtual resource declarations" do
+ has_class_rdoc("__site__", "test", /Resources.*?Notify\[virtual\]/m, /The virtual resource comment/)
+ end
+
+ it "documents resources" do
+ has_class_rdoc("__site__", "test", /Resources.*?Notify\[a_notify_resource\]/m, /message => "a_notify_resource message"/, /The a_notify_resource comment/)
+ end
+ end
end
- it "should produce a PuppetClass whose name is test" do
- get_test_class(@parser.scan).name.should == "test"
+ describe "rdoc1 support", :if => Puppet.features.rdoc1? do
+ def module_path(module_name); "#{doc_dir}/classes/#{module_name}.html" end
+ def node_path(module_name, node_name); "#{doc_dir}/nodes/**/*.html" end
+ def class_path(module_name, class_name); "#{doc_dir}/classes/#{module_name}/#{class_name}.html" end
+ def plugin_path(module_name, type, name); "#{doc_dir}/plugins/#{name}.html" end
+
+ include RdocTesters
+
+ def has_node_rdoc(module_name, node_name, *other_test_patterns)
+ some_file_exists_with_matching_content(node_path(module_name, node_name), /#{node_name}/, /node comment/, *other_test_patterns)
+ end
+
+ it_behaves_like :an_rdoc_site
+ it_behaves_like :an_rdoc1_site
+
+ it "references nodes and classes in the __site__ module" do
+ file_exists_and_matches_content("#{doc_dir}/classes/__site__.html", /Node.*__site__::foo/, /Class.*__site__::test/)
+ end
+
+ it "references functions, facts, and type plugins in the a_module module" do
+ file_exists_and_matches_content("#{doc_dir}/classes/a_module.html", /a_function/, /a_fact/, /a_plugin/, /Class.*a_module::a_module/)
+ end
end
- it "should produce a PuppetClass whose comment is 'comment'" do
- get_test_class(@parser.scan).comment.should == "comment\n"
+ describe "rdoc2 support", :if => !Puppet.features.rdoc1? do
+ def module_path(module_name); "#{doc_dir}/#{module_name}.html" end
+ def node_path(module_name, node_name); "#{doc_dir}/#{module_name}/__nodes__/#{node_name}.html" end
+ def class_path(module_name, class_name); "#{doc_dir}/#{module_name}/#{class_name}.html" end
+ def plugin_path(module_name, type, name); "#{doc_dir}/#{module_name}/__#{type}s__.html" end
+
+ include RdocTesters
+
+ it_behaves_like :an_rdoc_site
end
end
diff --git a/spec/integration/util/settings_spec.rb b/spec/integration/util/settings_spec.rb
index 360b78310..0da0938e9 100755
--- a/spec/integration/util/settings_spec.rb
+++ b/spec/integration/util/settings_spec.rb
@@ -41,7 +41,7 @@ describe Puppet::Settings do
settings.use(:main)
- expect(File.stat(settings[:maindir]).mode & 007777).to eq(Puppet.features.microsoft_windows? ? 0755 : 0750)
+ expect(Puppet::FileSystem::File.new(settings[:maindir]).stat.mode & 007777).to eq(Puppet.features.microsoft_windows? ? 0755 : 0750)
end
it "reparses configuration if configuration file is touched", :if => !Puppet.features.microsoft_windows? do
diff --git a/spec/integration/util/windows/process_spec.rb b/spec/integration/util/windows/process_spec.rb
new file mode 100644
index 000000000..6dc54d228
--- /dev/null
+++ b/spec/integration/util/windows/process_spec.rb
@@ -0,0 +1,22 @@
+#! /usr/bin/env ruby
+
+require 'spec_helper'
+require 'facter'
+
+describe "Puppet::Util::Windows::Process", :if => Puppet.features.microsoft_windows? do
+ describe "as an admin" do
+ it "should have the SeCreateSymbolicLinkPrivilege necessary to create symlinks on Vista / 2008+",
+ :if => Facter.value(:kernelmajversion).to_f >= 6.0 && Puppet.features.microsoft_windows? do
+ # this is a bit of a lame duck test since it requires running user to be admin
+ # a better integration test would create a new user with the privilege and verify
+ Puppet::Util::Windows::User.should be_admin
+ Puppet::Util::Windows::Process.process_privilege_symlink?.should be_true
+ end
+
+ it "should not have the SeCreateSymbolicLinkPrivilege necessary to create symlinks on 2003 and earlier",
+ :if => Facter.value(:kernelmajversion).to_f < 6.0 && Puppet.features.microsoft_windows? do
+ Puppet::Util::Windows::User.should be_admin
+ Puppet::Util::Windows::Process.process_privilege_symlink?.should be_false
+ end
+ end
+end
diff --git a/spec/integration/util/windows/security_spec.rb b/spec/integration/util/windows/security_spec.rb
index c2bd538ea..99d866f7f 100755
--- a/spec/integration/util/windows/security_spec.rb
+++ b/spec/integration/util/windows/security_spec.rb
@@ -16,31 +16,64 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
before :all do
@sids = {
:current_user => Puppet::Util::Windows::Security.name_to_sid(Sys::Admin.get_login),
+ :system => Win32::Security::SID::LocalSystem,
:admin => Puppet::Util::Windows::Security.name_to_sid("Administrator"),
+ :administrators => Win32::Security::SID::BuiltinAdministrators,
:guest => Puppet::Util::Windows::Security.name_to_sid("Guest"),
:users => Win32::Security::SID::BuiltinUsers,
:power_users => Win32::Security::SID::PowerUsers,
+ :none => Win32::Security::SID::Nobody,
+ :everyone => Win32::Security::SID::Everyone
}
end
let (:sids) { @sids }
let (:winsec) { WindowsSecurityTester.new }
+ def set_group_depending_on_current_user(path)
+ if sids[:current_user] == sids[:system]
+ # if the current user is SYSTEM, by setting the group to
+ # guest, SYSTEM is automagically given full control, so instead
+ # override that behavior with SYSTEM as group and a specific mode
+ winsec.set_group(sids[:system], path)
+ mode = winsec.get_mode(path)
+ winsec.set_mode(mode & ~WindowsSecurityTester::S_IRWXG, path)
+ else
+ winsec.set_group(sids[:guest], path)
+ end
+ end
+
shared_examples_for "only child owner" do
it "should allow child owner" do
- check_child_owner
+ winsec.set_owner(sids[:guest], parent)
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(0700, parent)
+
+ check_delete(path)
end
it "should deny parent owner" do
- lambda { check_parent_owner }.should raise_error(Errno::EACCES)
+ winsec.set_owner(sids[:guest], path)
+ winsec.set_group(sids[:current_user], path)
+ winsec.set_mode(0700, path)
+
+ lambda { check_delete(path) }.should raise_error(Errno::EACCES)
end
it "should deny group" do
- lambda { check_group }.should raise_error(Errno::EACCES)
+ winsec.set_owner(sids[:guest], path)
+ winsec.set_group(sids[:current_user], path)
+ winsec.set_mode(0700, path)
+
+ lambda { check_delete(path) }.should raise_error(Errno::EACCES)
end
it "should deny other" do
- lambda { check_other }.should raise_error(Errno::EACCES)
+ winsec.set_owner(sids[:guest], path)
+ winsec.set_group(sids[:current_user], path)
+ winsec.set_mode(0700, path)
+
+ lambda { check_delete(path) }.should raise_error(Errno::EACCES)
end
end
@@ -63,7 +96,7 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
after :each do
winsec.set_mode(WindowsSecurityTester::S_IRWXU, parent)
- winsec.set_mode(WindowsSecurityTester::S_IRWXU, path) if File.exists?(path)
+ winsec.set_mode(WindowsSecurityTester::S_IRWXU, path) if Puppet::FileSystem::File.exist?(path)
end
describe "#supports_acl?" do
@@ -122,6 +155,26 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
end
end
+ it "should preserve inherited full control for SYSTEM when setting owner and group" do
+ # new file has SYSTEM
+ system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
+ system_aces.should_not be_empty
+
+ # when running under SYSTEM account, multiple ACEs come back
+ # so we only care that we have at least one of these
+ system_aces.any? do |ace|
+ ace.mask == Windows::File::FILE_ALL_ACCESS
+ end.should be_true
+
+ # changing the owner/group will no longer make the SD protected
+ winsec.set_group(sids[:power_users], path)
+ winsec.set_owner(sids[:administrators], path)
+
+ system_aces.find do |ace|
+ ace.mask == Windows::File::FILE_ALL_ACCESS && ace.inherited?
+ end.should_not be_nil
+ end
+
describe "#mode=" do
(0000..0700).step(0100) do |mode|
it "should enforce mode #{mode.to_s(8)}" do
@@ -151,6 +204,28 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
end
end
+ it "should preserve full control for SYSTEM when setting mode" do
+ # new file has SYSTEM
+ system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
+ system_aces.should_not be_empty
+
+ # when running under SYSTEM account, multiple ACEs come back
+ # so we only care that we have at least one of these
+ system_aces.any? do |ace|
+ ace.mask == WindowsSecurityTester::FILE_ALL_ACCESS
+ end.should be_true
+
+ # changing the mode will make the SD protected
+ winsec.set_group(sids[:none], path)
+ winsec.set_mode(0600, path)
+
+ # and should have a non-inherited SYSTEM ACE(s)
+ system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
+ system_aces.each do |ace|
+ ace.mask.should == Windows::File::FILE_ALL_ACCESS && ! ace.inherited?
+ end
+ end
+
describe "for modes that require deny aces" do
it "should map everyone to group and owner" do
winsec.set_mode(0426, path)
@@ -167,6 +242,8 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
describe "for read-only objects" do
before :each do
+ winsec.set_group(sids[:none], path)
+ winsec.set_mode(0600, path)
winsec.add_attributes(path, WindowsSecurityTester::FILE_ATTRIBUTE_READONLY)
(winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should be_nonzero
end
@@ -176,9 +253,17 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
(winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should == 0
end
- it "should leave them read-only if no sid has write permission" do
+ it "should leave them read-only if no sid has write permission and should allow full access for SYSTEM" do
winsec.set_mode(WindowsSecurityTester::S_IRUSR | WindowsSecurityTester::S_IXGRP, path)
(winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should be_nonzero
+
+ system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
+
+ # when running under SYSTEM account, and set_group / set_owner hasn't been called
+ # SYSTEM full access will be restored
+ system_aces.any? do |ace|
+ ace.mask == Windows::File::FILE_ALL_ACCESS
+ end.should be_true
end
end
@@ -189,31 +274,39 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
describe "#mode" do
it "should report when extra aces are encounted" do
- winsec.set_acl(path, true) do |acl|
- (544..547).each do |rid|
- winsec.add_access_allowed_ace(acl, WindowsSecurityTester::STANDARD_RIGHTS_ALL, "S-1-5-32-#{rid}")
- end
+ sd = winsec.get_security_descriptor(path)
+ (544..547).each do |rid|
+ sd.dacl.allow("S-1-5-32-#{rid}", WindowsSecurityTester::STANDARD_RIGHTS_ALL)
end
+ winsec.set_security_descriptor(path, sd)
+
mode = winsec.get_mode(path)
- (mode & WindowsSecurityTester::S_IEXTRA).should_not == 0
+ (mode & WindowsSecurityTester::S_IEXTRA).should == WindowsSecurityTester::S_IEXTRA
end
- it "should warn if a deny ace is encountered" do
- winsec.set_acl(path) do |acl|
- winsec.add_access_denied_ace(acl, WindowsSecurityTester::FILE_GENERIC_WRITE, sids[:guest])
- winsec.add_access_allowed_ace(acl, WindowsSecurityTester::STANDARD_RIGHTS_ALL | WindowsSecurityTester::SPECIFIC_RIGHTS_ALL, sids[:current_user])
- end
-
- Puppet.expects(:warning).with("Unsupported access control entry type: 0x1")
+ it "should return deny aces" do
+ sd = winsec.get_security_descriptor(path)
+ sd.dacl.deny(sids[:guest], WindowsSecurityTester::FILE_GENERIC_WRITE)
+ winsec.set_security_descriptor(path, sd)
- winsec.get_mode(path)
+ guest_aces = winsec.get_aces_for_path_by_sid(path, sids[:guest])
+ guest_aces.find do |ace|
+ ace.type == WindowsSecurityTester::ACCESS_DENIED_ACE_TYPE
+ end.should_not be_nil
end
it "should skip inherit-only ace" do
- winsec.set_acl(path) do |acl|
- winsec.add_access_allowed_ace(acl, WindowsSecurityTester::STANDARD_RIGHTS_ALL | WindowsSecurityTester::SPECIFIC_RIGHTS_ALL, sids[:current_user])
- winsec.add_access_allowed_ace(acl, WindowsSecurityTester::FILE_GENERIC_READ, Win32::Security::SID::Everyone, WindowsSecurityTester::INHERIT_ONLY_ACE | WindowsSecurityTester::OBJECT_INHERIT_ACE)
- end
+ sd = winsec.get_security_descriptor(path)
+ dacl = Puppet::Util::Windows::AccessControlList.new
+ dacl.allow(
+ sids[:current_user], WindowsSecurityTester::STANDARD_RIGHTS_ALL | WindowsSecurityTester::SPECIFIC_RIGHTS_ALL
+ )
+ dacl.allow(
+ sids[:everyone],
+ WindowsSecurityTester::FILE_GENERIC_READ,
+ WindowsSecurityTester::INHERIT_ONLY_ACE | WindowsSecurityTester::OBJECT_INHERIT_ACE
+ )
+ winsec.set_security_descriptor(path, sd)
(winsec.get_mode(path) & WindowsSecurityTester::S_IRWXO).should == 0
end
@@ -224,9 +317,14 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
end
describe "inherited access control entries" do
- it "should be absent when the access control list is protected" do
+ it "should be absent when the access control list is protected, and should not remove SYSTEM" do
winsec.set_mode(WindowsSecurityTester::S_IRWXU, path)
- (winsec.get_mode(path) & WindowsSecurityTester::S_IEXTRA).should == 0
+
+ mode = winsec.get_mode(path)
+ [ WindowsSecurityTester::S_IEXTRA,
+ WindowsSecurityTester::S_ISYSTEM_MISSING ].each do |flag|
+ (mode & flag).should_not == flag
+ end
end
it "should be present when the access control list is unprotected" do
@@ -234,13 +332,20 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
allow = WindowsSecurityTester::STANDARD_RIGHTS_ALL | WindowsSecurityTester::SPECIFIC_RIGHTS_ALL
inherit = WindowsSecurityTester::OBJECT_INHERIT_ACE | WindowsSecurityTester::CONTAINER_INHERIT_ACE
- winsec.set_acl(parent, true) do |acl|
- winsec.add_access_allowed_ace(acl, allow, "S-1-1-0", inherit) # everyone
-
- (544..547).each do |rid|
- winsec.add_access_allowed_ace(acl, WindowsSecurityTester::STANDARD_RIGHTS_ALL, "S-1-5-32-#{rid}", inherit)
- end
+ sd = winsec.get_security_descriptor(parent)
+ sd.dacl.allow(
+ "S-1-1-0", #everyone
+ allow,
+ inherit
+ )
+ (544..547).each do |rid|
+ sd.dacl.allow(
+ "S-1-5-32-#{rid}",
+ WindowsSecurityTester::STANDARD_RIGHTS_ALL,
+ inherit
+ )
end
+ winsec.set_security_descriptor(parent, sd)
# unprotect child, it should inherit from parent
winsec.set_mode(WindowsSecurityTester::S_IRWXU, path, false)
@@ -252,13 +357,13 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
describe "for an administrator", :if => Puppet.features.root? do
before :each do
winsec.set_mode(WindowsSecurityTester::S_IRWXU | WindowsSecurityTester::S_IRWXG, path)
- winsec.set_group(sids[:guest], path)
+ set_group_depending_on_current_user(path)
winsec.set_owner(sids[:guest], path)
lambda { File.open(path, 'r') }.should raise_error(Errno::EACCES)
end
after :each do
- if File.exists?(path)
+ if Puppet::FileSystem::File.exist?(path)
winsec.set_owner(sids[:current_user], path)
winsec.set_mode(WindowsSecurityTester::S_IRWXU, path)
end
@@ -295,16 +400,18 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
winsec.get_group(path).should == sids[:admin]
end
- it "should allow owner and group to be the same sid" do
- winsec.set_mode(0610, path)
+ it "should combine owner and group rights when they are the same sid" do
winsec.set_owner(sids[:power_users], path)
winsec.set_group(sids[:power_users], path)
+ winsec.set_mode(0610, path)
winsec.get_owner(path).should == sids[:power_users]
winsec.get_group(path).should == sids[:power_users]
# note group execute permission added to user ace, and then group rwx value
# reflected to match
- winsec.get_mode(path).to_s(8).should == "770"
+
+ # Exclude missing system ace, since that's not relevant
+ (winsec.get_mode(path) & 0777).to_s(8).should == "770"
end
it "should raise an exception if an invalid sid is provided" do
@@ -355,10 +462,14 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
end
describe "#mode" do
- it "should deny all access when the DACL is empty" do
- winsec.set_acl(path, true) { |acl| }
+ it "should deny all access when the DACL is empty, including SYSTEM" do
+ sd = winsec.get_security_descriptor(path)
+ # don't allow inherited aces to affect the test
+ protect = true
+ new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, [], protect)
+ winsec.set_security_descriptor(path, new_sd)
- winsec.get_mode(path).should == 0
+ winsec.get_mode(path).should == WindowsSecurityTester::S_ISYSTEM_MISSING
end
# REMIND: ruby crashes when trying to set a NULL DACL
@@ -378,103 +489,121 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
winsec.set_mode(0777, path, false)
end
- def check_child_owner
- winsec.set_group(sids[:guest], parent)
- winsec.set_owner(sids[:guest], parent)
+ describe "is writable and executable" do
+ describe "and sticky bit is set" do
+ it "should allow child owner" do
+ winsec.set_owner(sids[:guest], parent)
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(01700, parent)
- check_delete(path)
- end
+ check_delete(path)
+ end
- def check_parent_owner
- winsec.set_group(sids[:guest], path)
- winsec.set_owner(sids[:guest], path)
+ it "should allow parent owner" do
+ winsec.set_owner(sids[:current_user], parent)
+ winsec.set_group(sids[:guest], parent)
+ winsec.set_mode(01700, parent)
- check_delete(path)
- end
+ winsec.set_owner(sids[:current_user], path)
+ winsec.set_group(sids[:guest], path)
+ winsec.set_mode(0700, path)
- def check_group
- winsec.set_group(sids[:current_user], path)
- winsec.set_owner(sids[:guest], path)
+ check_delete(path)
+ end
- winsec.set_owner(sids[:guest], parent)
+ it "should deny group" do
+ winsec.set_owner(sids[:guest], parent)
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(01770, parent)
- check_delete(path)
- end
+ winsec.set_owner(sids[:guest], path)
+ winsec.set_group(sids[:current_user], path)
+ winsec.set_mode(0700, path)
- def check_other
- winsec.set_group(sids[:guest], path)
- winsec.set_owner(sids[:guest], path)
+ lambda { check_delete(path) }.should raise_error(Errno::EACCES)
+ end
- winsec.set_owner(sids[:guest], parent)
+ it "should deny other" do
+ winsec.set_owner(sids[:guest], parent)
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(01777, parent)
- check_delete(path)
- end
+ winsec.set_owner(sids[:guest], path)
+ winsec.set_group(sids[:current_user], path)
+ winsec.set_mode(0700, path)
- describe "is writable and executable" do
- describe "and sticky bit is set" do
- before :each do
- winsec.set_mode(01777, parent)
+ lambda { check_delete(path) }.should raise_error(Errno::EACCES)
+ end
end
- it "should allow child owner" do
- check_child_owner
- end
+ describe "and sticky bit is not set" do
+ it "should allow child owner" do
+ winsec.set_owner(sids[:guest], parent)
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(0700, parent)
- it "should allow parent owner" do
- check_parent_owner
- end
+ check_delete(path)
+ end
- it "should deny group" do
- lambda { check_group }.should raise_error(Errno::EACCES)
- end
+ it "should allow parent owner" do
+ winsec.set_owner(sids[:current_user], parent)
+ winsec.set_group(sids[:guest], parent)
+ winsec.set_mode(0700, parent)
- it "should deny other" do
- lambda { check_other }.should raise_error(Errno::EACCES)
- end
- end
+ winsec.set_owner(sids[:current_user], path)
+ winsec.set_group(sids[:guest], path)
+ winsec.set_mode(0700, path)
- describe "and sticky bit is not set" do
- before :each do
- winsec.set_mode(0777, parent)
- end
+ check_delete(path)
+ end
- it "should allow child owner" do
- check_child_owner
- end
+ it "should allow group" do
+ winsec.set_owner(sids[:guest], parent)
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(0770, parent)
- it "should allow parent owner" do
- check_parent_owner
- end
+ winsec.set_owner(sids[:guest], path)
+ winsec.set_group(sids[:current_user], path)
+ winsec.set_mode(0700, path)
- it "should allow group" do
- check_group
- end
+ check_delete(path)
+ end
- it "should allow other" do
- check_other
+ it "should allow other" do
+ winsec.set_owner(sids[:guest], parent)
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(0777, parent)
+
+ winsec.set_owner(sids[:guest], path)
+ winsec.set_group(sids[:current_user], path)
+ winsec.set_mode(0700, path)
+
+ check_delete(path)
+ end
end
end
- end
- describe "is not writable" do
- before :each do
- winsec.set_mode(0555, parent)
+ describe "is not writable" do
+ before :each do
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(0555, parent)
+ end
+
+ it_behaves_like "only child owner"
end
- it_behaves_like "only child owner"
- end
+ describe "is not executable" do
+ before :each do
+ winsec.set_group(sids[:current_user], parent)
+ winsec.set_mode(0666, parent)
+ end
- describe "is not executable" do
- before :each do
- winsec.set_mode(0666, parent)
+ it_behaves_like "only child owner"
end
-
- it_behaves_like "only child owner"
end
end
end
end
- end
describe "file" do
let (:parent) do
@@ -603,9 +732,90 @@ describe "Puppet::Util::Windows::Security", :if => Puppet.features.microsoft_win
Dir.mkdir(newdir)
[newfile, newdir].each do |p|
- winsec.get_mode(p).to_s(8).should == mode640.to_s(8)
+ mode = winsec.get_mode(p)
+ (mode & 07777).to_s(8).should == mode640.to_s(8)
end
end
end
end
+
+ context "security descriptor" do
+ let(:path) { tmpfile('sec_descriptor') }
+ let(:read_execute) { 0x201FF }
+ let(:synchronize) { 0x100000 }
+
+ before :each do
+ FileUtils.touch(path)
+ end
+
+ it "preserves aces for other users" do
+ dacl = Puppet::Util::Windows::AccessControlList.new
+ sids_in_dacl = [sids[:current_user], sids[:users]]
+ sids_in_dacl.each do |sid|
+ dacl.allow(sid, read_execute)
+ end
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(sids[:guest], sids[:guest], dacl, true)
+ winsec.set_security_descriptor(path, sd)
+
+ aces = winsec.get_security_descriptor(path).dacl.to_a
+ aces.map(&:sid).should == sids_in_dacl
+ aces.map(&:mask).all? { |mask| mask == read_execute }.should be_true
+ end
+
+ it "changes the sid for all aces that were assigned to the old owner" do
+ sd = winsec.get_security_descriptor(path)
+ sd.owner.should_not == sids[:guest]
+
+ sd.dacl.allow(sd.owner, read_execute)
+ sd.dacl.allow(sd.owner, synchronize)
+
+ sd.owner = sids[:guest]
+ winsec.set_security_descriptor(path, sd)
+
+ dacl = winsec.get_security_descriptor(path).dacl
+ aces = dacl.find_all { |ace| ace.sid == sids[:guest] }
+ # only non-inherited aces will be reassigned to guest, so
+ # make sure we find at least the two we added
+ aces.size.should >= 2
+ end
+
+ it "preserves INHERIT_ONLY_ACEs" do
+ # inherit only aces can only be set on directories
+ dir = tmpdir('inheritonlyace')
+
+ inherit_flags = Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE |
+ Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |
+ Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE
+
+ sd = winsec.get_security_descriptor(dir)
+ sd.dacl.allow(sd.owner, Windows::File::FILE_ALL_ACCESS, inherit_flags)
+ winsec.set_security_descriptor(dir, sd)
+
+ sd = winsec.get_security_descriptor(dir)
+
+ winsec.set_owner(sids[:guest], dir)
+
+ sd = winsec.get_security_descriptor(dir)
+ sd.dacl.find do |ace|
+ ace.sid == sids[:guest] && ace.inherit_only?
+ end.should_not be_nil
+ end
+
+ context "when managing mode" do
+ it "removes aces for sids that are neither the owner nor group" do
+ # add a guest ace, it's never owner or group
+ sd = winsec.get_security_descriptor(path)
+ sd.dacl.allow(sids[:guest], read_execute)
+ winsec.set_security_descriptor(path, sd)
+
+ # setting the mode, it should remove extra aces
+ winsec.set_mode(0770, path)
+
+ # make sure it's gone
+ dacl = winsec.get_security_descriptor(path).dacl
+ aces = dacl.find_all { |ace| ace.sid == sids[:guest] }
+ aces.should be_empty
+ end
+ end
+ end
end
diff --git a/spec/lib/matchers/containment_matchers.rb b/spec/lib/matchers/containment_matchers.rb
new file mode 100644
index 000000000..bc412fc5e
--- /dev/null
+++ b/spec/lib/matchers/containment_matchers.rb
@@ -0,0 +1,52 @@
+module ContainmentMatchers
+ class ContainClass
+ def initialize(containee)
+ @containee = containee
+ end
+
+ def in(container)
+ @container = container
+ self
+ end
+
+ def matches?(catalog)
+ @catalog = catalog
+
+ raise ArgumentError, "You must set the container using #in" unless @container
+
+ @container_resource = catalog.resource("Class", @container)
+ @containee_resource = catalog.resource("Class", @containee)
+
+ if @containee_resource && @container_resource
+ catalog.edge?(@container_resource, @containee_resource)
+ else
+ false
+ end
+ end
+
+ def failure_message_for_should
+ message = "Expected #{@catalog.to_dot} to contain Class #{@containee.inspect} inside of Class #{@container.inspect} but "
+
+ missing = []
+ if @container_resource.nil?
+ missing << @container
+ end
+ if @containee_resource.nil?
+ missing << @containee
+ end
+
+ if ! missing.empty?
+ message << "the catalog does not contain #{missing.map(&:inspect).join(' or ')}"
+ else
+ message << "no containment relationship exists"
+ end
+
+ message
+ end
+ end
+
+ # expect(catalog).to contain_class(containee).in(container)
+ def contain_class(containee)
+ ContainClass.new(containee)
+ end
+end
diff --git a/spec/lib/puppet_spec/compiler.rb b/spec/lib/puppet_spec/compiler.rb
index 0286fbc8e..cf4851807 100644
--- a/spec/lib/puppet_spec/compiler.rb
+++ b/spec/lib/puppet_spec/compiler.rb
@@ -27,4 +27,10 @@ module PuppetSpec::Compiler
transaction
end
+
+ def order_resources_traversed_in(relationships)
+ order_seen = []
+ relationships.traverse { |resource| order_seen << resource.ref }
+ order_seen
+ end
end
diff --git a/spec/lib/puppet_spec/files.rb b/spec/lib/puppet_spec/files.rb
index 196d4b9c4..afe86fba2 100755
--- a/spec/lib/puppet_spec/files.rb
+++ b/spec/lib/puppet_spec/files.rb
@@ -1,26 +1,13 @@
require 'fileutils'
require 'tempfile'
+require 'tmpdir'
require 'pathname'
# 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
- def self.in_tmp(path)
- tempdir = Dir.tmpdir
-
- Pathname.new(path).ascend do |dir|
- return true if File.identical?(tempdir, dir)
- end
-
- false
- 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_rf path, :secure => true
rescue Errno::ENOENT
@@ -43,18 +30,30 @@ module PuppetSpec::Files
path = source.path
source.close!
- # ...record it for cleanup,
- $global_tempfiles ||= []
- $global_tempfiles << File.expand_path(path)
+ record_tmp(File.expand_path(path))
- # ...and bam.
path
end
+ def file_containing(name, contents) PuppetSpec::Files.file_containing(name, contents) end
+ def self.file_containing(name, contents)
+ file = tmpfile(name)
+ File.open(file, 'wb') { |f| f.write(contents) }
+ file
+ end
+
def tmpdir(name) PuppetSpec::Files.tmpdir(name) end
def self.tmpdir(name)
- path = tmpfile(name)
- FileUtils.mkdir_p(path)
- path
+ dir = Dir.mktmpdir(name)
+
+ record_tmp(dir)
+
+ dir
+ end
+
+ def self.record_tmp(tmp)
+ # ...record it for cleanup,
+ $global_tempfiles ||= []
+ $global_tempfiles << tmp
end
end
diff --git a/spec/shared_behaviours/documentation_on_faces.rb b/spec/shared_behaviours/documentation_on_faces.rb
index 204b173a5..7c4d9e04b 100644
--- a/spec/shared_behaviours/documentation_on_faces.rb
+++ b/spec/shared_behaviours/documentation_on_faces.rb
@@ -62,7 +62,7 @@ shared_examples_for "documentation on faces" do
end
it "should not remove formatting whitespace, only global indent" do
- text = "this\n is\n the\n ultimate\ntest\n"
+ text = "this\n is\n the\n ultimate\ntest"
subject.send(setter, text.gsub(/^/, indent))
subject.send(getter).should == text
end
@@ -70,8 +70,8 @@ shared_examples_for "documentation on faces" do
end
it "should strip whitespace with a blank line" do
- subject.send(setter, " this\n\n should outdent\n")
- subject.send(getter).should == "this\n\nshould outdent\n"
+ subject.send(setter, " this\n\n should outdent")
+ subject.send(getter).should == "this\n\nshould outdent"
end
end
end
diff --git a/spec/shared_behaviours/file_server_terminus.rb b/spec/shared_behaviours/file_server_terminus.rb
index 8266a75d9..ff122f8ad 100755
--- a/spec/shared_behaviours/file_server_terminus.rb
+++ b/spec/shared_behaviours/file_server_terminus.rb
@@ -4,8 +4,8 @@ shared_examples_for "Puppet::Indirector::FileServerTerminus" do
# the 'before' block in the including context.
before do
Puppet::FileServing::Configuration.instance_variable_set(:@configuration, nil)
- FileTest.stubs(:exists?).returns true
- FileTest.stubs(:exists?).with(Puppet[:fileserverconfig]).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:fileserverconfig]).returns(true)
@path = Tempfile.new("file_server_testing")
path = @path.path
diff --git a/spec/shared_contexts/platform.rb b/spec/shared_contexts/platform.rb
index 762eddfbd..2e6d4d7a6 100644
--- a/spec/shared_contexts/platform.rb
+++ b/spec/shared_contexts/platform.rb
@@ -6,6 +6,7 @@
shared_context "windows", :as_platform => :windows do
before :each do
Facter.stubs(:value).with(:operatingsystem).returns 'Windows'
+ Facter.stubs(:value).with(:osfamily).returns 'windows'
Puppet.features.stubs(:microsoft_windows?).returns(true)
Puppet.features.stubs(:posix?).returns(false)
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 7b4bd7219..49fa482cf 100755
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -43,6 +43,18 @@ Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour|
require behaviour.relative_path_from(Pathname.new(dir))
end
+# various spec tests now use json schema validation
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ require 'json'
+ require 'json-schema'
+
+ JSON_META_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../api/schemas/json-meta-schema.json')))
+
+ # FACTS_SCHEMA is shared across two spec files so promote constant to here
+ FACTS_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../api/schemas/facts.json')))
+end
+
RSpec.configure do |config|
include PuppetSpec::Fixtures
@@ -146,6 +158,6 @@ RSpec.configure do |config|
# Clean up switch of TMPDIR, don't know if needed after this, so needs to reset it
# to old before removing it
ENV['TMPDIR'] = oldtmpdir
- FileUtils.rm_rf(tmpdir) if File.exists?(tmpdir) && tmpdir.to_s.start_with?(oldtmpdir)
+ FileUtils.rm_rf(tmpdir) if Puppet::FileSystem::File.exist?(tmpdir) && tmpdir.to_s.start_with?(oldtmpdir)
end
end
diff --git a/spec/unit/agent_spec.rb b/spec/unit/agent_spec.rb
index 536fb47de..38e53176d 100755
--- a/spec/unit/agent_spec.rb
+++ b/spec/unit/agent_spec.rb
@@ -135,18 +135,6 @@ describe Puppet::Agent do
@agent.run
end
- it "should use a mutex to restrict multi-threading" do
- client = AgentTestClient.new
- AgentTestClient.expects(:new).returns client
-
- mutex = mock 'mutex'
- @agent.expects(:sync).returns mutex
-
- mutex.expects(:synchronize)
- client.expects(:run).never # if it doesn't run, then we know our yield is what triggers it
- @agent.run
- end
-
it "should use a filesystem lock to restrict multiple processes running the agent" do
client = AgentTestClient.new
AgentTestClient.expects(:new).returns client
diff --git a/spec/unit/application/agent_spec.rb b/spec/unit/application/agent_spec.rb
index 2ec32dbfb..4280a953c 100755
--- a/spec/unit/application/agent_spec.rb
+++ b/spec/unit/application/agent_spec.rb
@@ -198,7 +198,7 @@ describe Puppet::Application::Agent do
describe "during setup" do
before :each do
Puppet.stubs(:info)
- FileTest.stubs(:exists?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
Puppet[:libdir] = "/dev/null/lib"
Puppet::Transaction::Report.indirection.stubs(:terminus_class=)
Puppet::Transaction::Report.indirection.stubs(:cache_class=)
@@ -449,8 +449,8 @@ describe Puppet::Application::Agent do
describe "when setting up listen" do
before :each do
- FileTest.stubs(:exists?).with('auth').returns(true)
- File.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with('auth').returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
@puppetd.options[:serve] = []
@server = stub_everything 'server'
Puppet::Network::Server.stubs(:new).returns(@server)
@@ -460,7 +460,7 @@ describe Puppet::Application::Agent do
it "should exit if no authorization file" do
Puppet[:listen] = true
Puppet.stubs(:err)
- FileTest.stubs(:exists?).with(Puppet[:rest_authconfig]).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:rest_authconfig]).returns(false)
expect do
execute_agent
diff --git a/spec/unit/application/apply_spec.rb b/spec/unit/application/apply_spec.rb
index 97728c06e..d3cc54884 100755
--- a/spec/unit/application/apply_spec.rb
+++ b/spec/unit/application/apply_spec.rb
@@ -45,8 +45,8 @@ describe Puppet::Application::Apply do
@apply.handle_logdest("console")
end
- it "should put the logset options to true" do
- @apply.options.expects(:[]=).with(:logset,true)
+ it "should set the setdest options to true" do
+ @apply.options.expects(:[]=).with(:setdest,true)
@apply.handle_logdest("console")
end
@@ -103,6 +103,22 @@ describe Puppet::Application::Apply do
@apply.setup
end
+ it "configures a profiler when profiling is enabled" do
+ Puppet[:profile] = true
+
+ @apply.setup
+
+ expect(Puppet::Util::Profiler.current).to be_a(Puppet::Util::Profiler::WallClock)
+ end
+
+ it "does not have a profiler if profiling is disabled" do
+ Puppet[:profile] = false
+
+ @apply.setup
+
+ expect(Puppet::Util::Profiler.current).to eq(Puppet::Util::Profiler::NONE)
+ end
+
it "should set default_file_terminus to `file_server` to be local" do
@apply.app_defaults[:default_file_terminus].should == :file_server
end
diff --git a/spec/unit/application/cert_spec.rb b/spec/unit/application/cert_spec.rb
index 25d74c859..f24ef8677 100755
--- a/spec/unit/application/cert_spec.rb
+++ b/spec/unit/application/cert_spec.rb
@@ -122,10 +122,12 @@ describe Puppet::Application::Cert => true do
@ca = stub_everything 'ca'
@cert_app.ca = @ca
@cert_app.command_line.stubs(:args).returns([])
+ @iface = stub_everything 'iface'
+ Puppet::SSL::CertificateAuthority::Interface.stubs(:new).returns(@iface)
end
it "should delegate to the CertificateAuthority" do
- @ca.expects(:apply)
+ @iface.expects(:apply)
@cert_app.main
end
@@ -133,7 +135,7 @@ describe Puppet::Application::Cert => true do
it "should delegate with :all if option --all was given" do
@cert_app.handle_all(0)
- @ca.expects(:apply).with { |cert_mode,to| to[:to] == :all }
+ Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns(@iface).with { |cert_mode,to| to[:to] == :all }
@cert_app.main
end
@@ -141,7 +143,7 @@ describe Puppet::Application::Cert => true do
it "should delegate to ca.apply with the hosts given on command line" do
@cert_app.command_line.stubs(:args).returns(["host"])
- @ca.expects(:apply).with { |cert_mode,to| to[:to] == ["host"]}
+ Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns(@iface).with { |cert_mode,to| to[:to] == ["host"]}
@cert_app.main
end
@@ -150,7 +152,7 @@ describe Puppet::Application::Cert => true do
@cert_app.command_line.stubs(:args).returns(["host"])
@cert_app.handle_digest(:digest)
- @ca.expects(:apply).with { |cert_mode,to| to[:digest] == :digest}
+ Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns(@iface).with { |cert_mode,to| to[:digest] == :digest}
@cert_app.main
end
@@ -159,8 +161,8 @@ describe Puppet::Application::Cert => true do
@cert_app.subcommand = :destroy
@cert_app.command_line.stubs(:args).returns(["host"])
- @ca.expects(:apply).with { |cert_mode,to| cert_mode == :revoke }
- @ca.expects(:apply).with { |cert_mode,to| cert_mode == :destroy }
+ Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns(@iface).with { |cert_mode,to| cert_mode == :revoke }
+ Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns(@iface).with { |cert_mode,to| cert_mode == :destroy }
@cert_app.main
end
diff --git a/spec/unit/application/device_spec.rb b/spec/unit/application/device_spec.rb
index 43787b69f..bbbc011c1 100755
--- a/spec/unit/application/device_spec.rb
+++ b/spec/unit/application/device_spec.rb
@@ -127,7 +127,7 @@ describe Puppet::Application::Device do
before :each do
@device.options.stubs(:[])
Puppet.stubs(:info)
- FileTest.stubs(:exists?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
Puppet[:libdir] = "/dev/null/lib"
Puppet::SSL::Host.stubs(:ca_location=)
Puppet::Transaction::Report.indirection.stubs(:terminus_class=)
diff --git a/spec/unit/application/filebucket_spec.rb b/spec/unit/application/filebucket_spec.rb
index e7702e45a..4301c7dcd 100755
--- a/spec/unit/application/filebucket_spec.rb
+++ b/spec/unit/application/filebucket_spec.rb
@@ -178,7 +178,7 @@ describe Puppet::Application::Filebucket do
it "should call the client backup method for each given parameter" do
@filebucket.stubs(:puts)
- FileTest.stubs(:exists?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
FileTest.stubs(:readable?).returns(true)
@filebucket.stubs(:args).returns(["file1", "file2"])
diff --git a/spec/unit/application/inspect_spec.rb b/spec/unit/application/inspect_spec.rb
index c0a034511..da73ee51c 100755
--- a/spec/unit/application/inspect_spec.rb
+++ b/spec/unit/application/inspect_spec.rb
@@ -36,7 +36,7 @@ describe Puppet::Application::Inspect do
describe "when executing" do
before :each do
Puppet[:report] = true
- @inspect.options[:logset] = true
+ @inspect.options[:setdest] = true
Puppet::Transaction::Report::Rest.any_instance.stubs(:save)
@inspect.setup
end
diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb
index 912416d53..1c9dd49b8 100755
--- a/spec/unit/application_spec.rb
+++ b/spec/unit/application_spec.rb
@@ -618,4 +618,28 @@ describe Puppet::Application do
end
end
+
+ describe "#handle_logdest_arg" do
+
+ let(:test_arg) { "arg_test_logdest" }
+
+ it "should log an exception that is raised" do
+ our_exception = Puppet::DevError.new("test exception")
+ Puppet::Util::Log.expects(:newdestination).with(test_arg).raises(our_exception)
+ Puppet.expects(:log_exception).with(our_exception)
+ @app.handle_logdest_arg(test_arg)
+ end
+
+ it "should set the new log destination" do
+ Puppet::Util::Log.expects(:newdestination).with(test_arg)
+ @app.handle_logdest_arg(test_arg)
+ end
+
+ it "should set the flag that a destination is set in the options hash" do
+ Puppet::Util::Log.stubs(:newdestination).with(test_arg)
+ @app.handle_logdest_arg(test_arg)
+ @app.options[:setdest].should be_true
+ end
+ end
+
end
diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb
index 403e92c1a..a818370fc 100755
--- a/spec/unit/configurer/downloader_spec.rb
+++ b/spec/unit/configurer/downloader_spec.rb
@@ -51,11 +51,7 @@ describe Puppet::Configurer::Downloader do
@dler.file
end
- describe "on POSIX" do
- before :each do
- Puppet.features.stubs(:microsoft_windows?).returns false
- end
-
+ describe "on POSIX", :as_platform => :posix do
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 }
@@ -69,7 +65,7 @@ describe Puppet::Configurer::Downloader do
end
end
- describe "on Windows", :if => Puppet.features.microsoft_windows? do
+ describe "on Windows", :as_platform => :windows do
it "should omit the owner" do
Puppet::Type.type(:file).expects(:new).with { |opts| opts[:owner] == nil }
@dler.file
@@ -79,6 +75,11 @@ describe Puppet::Configurer::Downloader do
Puppet::Type.type(:file).expects(:new).with { |opts| opts[:group] == nil }
@dler.file
end
+
+ it "should set source_permissions to ignore" do
+ Puppet::Type.type(:file).expects(:new).with { |opts| opts[:source_permissions] == :ignore }
+ @dler.file
+ end
end
it "should always force the download" do
@@ -135,7 +136,7 @@ describe Puppet::Configurer::Downloader do
Puppet[:tags] = 'maytag'
@dler.evaluate
- File.exists?(@dl_name).should be_true
+ Puppet::FileSystem::File.exist?(@dl_name).should be_true
end
it "should log that it is downloading" do
diff --git a/spec/unit/configurer/fact_handler_spec.rb b/spec/unit/configurer/fact_handler_spec.rb
index a74a91dfa..a92ad7d4f 100755
--- a/spec/unit/configurer/fact_handler_spec.rb
+++ b/spec/unit/configurer/fact_handler_spec.rb
@@ -3,6 +3,16 @@ require 'spec_helper'
require 'puppet/configurer'
require 'puppet/configurer/fact_handler'
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ describe "catalog facts schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, FACTS_SCHEMA)
+ end
+ end
+
+ end
+
class FactHandlerTester
include Puppet::Configurer::FactHandler
@@ -74,4 +84,17 @@ describe Puppet::Configurer::FactHandler do
@facthandler.facts_for_uploading.should == {:facts_format => :pson, :facts => text}
end
+
+ def validate_json_for_facts(catalog_facts)
+ JSON::Validator.validate!(FACTS_SCHEMA, catalog_facts)
+ end
+
+ it "should generate valid facts data against the facts schema", :unless => Puppet.features.microsoft_windows? do
+ facts = Puppet::Node::Facts.new(Puppet[:node_name_value], 'my_name_fact' => 'other_node_name')
+ Puppet::Node::Facts.indirection.save(facts)
+
+ validate_json_for_facts(CGI.unescape(@facthandler.facts_for_uploading[:facts]))
+ end
+
end
+
diff --git a/spec/unit/configurer/plugin_handler_spec.rb b/spec/unit/configurer/plugin_handler_spec.rb
index e5fda57e2..20027333d 100755
--- a/spec/unit/configurer/plugin_handler_spec.rb
+++ b/spec/unit/configurer/plugin_handler_spec.rb
@@ -19,7 +19,7 @@ describe Puppet::Configurer::PluginHandler do
it "should use an Agent Downloader, with the name, source, destination, ignore, and environment set correctly, to download plugins when downloading is enabled" do
downloader = mock 'downloader'
-
+ Puppet.features.stubs(:external_facts?).returns(:true)
# This is needed in order to make sure we pass on windows
plugindest = File.expand_path("/tmp/pdest")
@@ -27,9 +27,14 @@ describe Puppet::Configurer::PluginHandler do
Puppet[:plugindest] = plugindest
Puppet[:pluginsignore] = "pignore"
+ Puppet[:pluginfactsource] = "psource"
+ Puppet[:pluginfactdest] = plugindest
+
+ Puppet::Configurer::Downloader.expects(:new).with("pluginfacts", plugindest, "psource", "pignore", "myenv").returns downloader
Puppet::Configurer::Downloader.expects(:new).with("plugin", plugindest, "psource", "pignore", "myenv").returns downloader
- downloader.expects(:evaluate).returns []
+ downloader.stubs(:evaluate).returns([])
+ downloader.expects(:evaluate).twice
@pluginhandler.environment = "myenv"
@pluginhandler.download_plugins
diff --git a/spec/unit/configurer_spec.rb b/spec/unit/configurer_spec.rb
index cba1df584..7732bea08 100755
--- a/spec/unit/configurer_spec.rb
+++ b/spec/unit/configurer_spec.rb
@@ -110,6 +110,16 @@ describe Puppet::Configurer do
@agent.run.should == 0
end
+ it "applies a cached catalog when it can't connect to the master" do
+ error = Errno::ECONNREFUSED.new('Connection refused - connect(2)')
+
+ Puppet::Node.indirection.expects(:find).raises(error)
+ Puppet::Resource::Catalog.indirection.expects(:find).with(anything, has_entry(:ignore_cache => true)).raises(error)
+ Puppet::Resource::Catalog.indirection.expects(:find).with(anything, has_entry(:ignore_terminus => true)).returns(@catalog)
+
+ @agent.run.should == 0
+ end
+
it "should initialize a transaction report if one is not provided" do
report = Puppet::Transaction::Report.new("apply")
Puppet::Transaction::Report.expects(:new).returns report
@@ -341,12 +351,12 @@ describe Puppet::Configurer do
@agent.environment.should == "second_env"
end
- it "should clear the thread local caches" do
- Thread.current[:env_module_directories] = false
+ it "should clear the global caches" do
+ $env_module_directories = false
@agent.run
- Thread.current[:env_module_directories].should == nil
+ $env_module_directories.should == nil
end
describe "when not using a REST terminus for catalogs" do
@@ -454,7 +464,7 @@ describe Puppet::Configurer do
it "should write the last run file" do
@configurer.save_last_run_summary(@report)
- FileTest.exists?(Puppet[:lastrunfile]).should be_true
+ Puppet::FileSystem::File.exist?(Puppet[:lastrunfile]).should be_true
end
it "should write the raw summary as yaml" do
@@ -486,7 +496,7 @@ describe Puppet::Configurer do
require 'puppet/util/windows/security'
mode = Puppet::Util::Windows::Security.get_mode(Puppet[:lastrunfile])
else
- mode = File.stat(Puppet[:lastrunfile]).mode
+ mode = Puppet::FileSystem::File.new(Puppet[:lastrunfile]).stat.mode
end
(mode & 0777).should == 0664
end
diff --git a/spec/unit/provider/confine/exists_spec.rb b/spec/unit/confine/exists_spec.rb
index 17fd3f562..87959fe34 100755
--- a/spec/unit/provider/confine/exists_spec.rb
+++ b/spec/unit/confine/exists_spec.rb
@@ -1,20 +1,20 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confine/exists'
+require 'puppet/confine/exists'
-describe Puppet::Provider::Confine::Exists do
+describe Puppet::Confine::Exists do
before do
- @confine = Puppet::Provider::Confine::Exists.new("/my/file")
+ @confine = Puppet::Confine::Exists.new("/my/file")
@confine.label = "eh"
end
it "should be named :exists" do
- Puppet::Provider::Confine::Exists.name.should == :exists
+ Puppet::Confine::Exists.name.should == :exists
end
it "should not pass if exists is nil" do
- confine = Puppet::Provider::Confine::Exists.new(nil)
+ confine = Puppet::Confine::Exists.new(nil)
confine.label = ":exists => nil"
confine.expects(:pass?).with(nil)
confine.should_not be_valid
@@ -30,12 +30,12 @@ describe Puppet::Provider::Confine::Exists do
end
it "should return false if the value does not point to a file" do
- FileTest.expects(:exist?).with("/my/file").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("/my/file").returns false
@confine.pass?("/my/file").should be_false
end
it "should return true if the value points to a file" do
- FileTest.expects(:exist?).with("/my/file").returns true
+ Puppet::FileSystem::File.expects(:exist?).with("/my/file").returns true
@confine.pass?("/my/file").should be_true
end
@@ -62,11 +62,11 @@ describe Puppet::Provider::Confine::Exists do
end
it "should produce a summary containing all missing files" do
- FileTest.stubs(:exist?).returns true
- FileTest.expects(:exist?).with("/two").returns false
- FileTest.expects(:exist?).with("/four").returns false
+ Puppet::FileSystem::File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.expects(:exist?).with("/two").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("/four").returns false
- confine = Puppet::Provider::Confine::Exists.new %w{/one /two /three /four}
+ confine = Puppet::Confine::Exists.new %w{/one /two /three /four}
confine.summary.should == %w{/two /four}
end
@@ -75,6 +75,6 @@ describe Puppet::Provider::Confine::Exists do
c2 = mock '2', :summary => %w{two}
c3 = mock '3', :summary => %w{three}
- Puppet::Provider::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three}
+ Puppet::Confine::Exists.summarize([c1, c2, c3]).should == %w{one two three}
end
end
diff --git a/spec/unit/provider/confine/false_spec.rb b/spec/unit/confine/false_spec.rb
index 91d738c52..44ecd40ed 100755
--- a/spec/unit/provider/confine/false_spec.rb
+++ b/spec/unit/confine/false_spec.rb
@@ -1,22 +1,22 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confine/false'
+require 'puppet/confine/false'
-describe Puppet::Provider::Confine::False do
+describe Puppet::Confine::False do
it "should be named :false" do
- Puppet::Provider::Confine::False.name.should == :false
+ Puppet::Confine::False.name.should == :false
end
it "should require a value" do
- lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError)
+ lambda { Puppet::Confine.new }.should raise_error(ArgumentError)
end
describe "when testing values" do
- before { @confine = Puppet::Provider::Confine::False.new("foo") }
+ before { @confine = Puppet::Confine::False.new("foo") }
it "should use the 'pass?' method to test validity" do
- @confine = Puppet::Provider::Confine::False.new("foo")
+ @confine = Puppet::Confine::False.new("foo")
@confine.label = "eh"
@confine.expects(:pass?).with("foo")
@confine.valid?
@@ -31,13 +31,13 @@ describe Puppet::Provider::Confine::False do
end
it "should produce a message that a value is true" do
- @confine = Puppet::Provider::Confine::False.new("foo")
+ @confine = Puppet::Confine::False.new("foo")
@confine.message("eh").should be_include("true")
end
end
it "should be able to produce a summary with the number of incorrectly true values" do
- confine = Puppet::Provider::Confine::False.new %w{one two three four}
+ confine = Puppet::Confine::False.new %w{one two three four}
confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false)
confine.summary.should == 2
end
@@ -47,6 +47,6 @@ describe Puppet::Provider::Confine::False do
c2 = mock '2', :summary => 2
c3 = mock '3', :summary => 3
- Puppet::Provider::Confine::False.summarize([c1, c2, c3]).should == 6
+ Puppet::Confine::False.summarize([c1, c2, c3]).should == 6
end
end
diff --git a/spec/unit/provider/confine/feature_spec.rb b/spec/unit/confine/feature_spec.rb
index b90662235..dad9d9b8b 100755
--- a/spec/unit/provider/confine/feature_spec.rb
+++ b/spec/unit/confine/feature_spec.rb
@@ -1,24 +1,24 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confine/feature'
+require 'puppet/confine/feature'
-describe Puppet::Provider::Confine::Feature do
+describe Puppet::Confine::Feature do
it "should be named :feature" do
- Puppet::Provider::Confine::Feature.name.should == :feature
+ Puppet::Confine::Feature.name.should == :feature
end
it "should require a value" do
- lambda { Puppet::Provider::Confine::Feature.new }.should raise_error(ArgumentError)
+ lambda { Puppet::Confine::Feature.new }.should raise_error(ArgumentError)
end
it "should always convert values to an array" do
- Puppet::Provider::Confine::Feature.new("/some/file").values.should be_instance_of(Array)
+ Puppet::Confine::Feature.new("/some/file").values.should be_instance_of(Array)
end
describe "when testing values" do
before do
- @confine = Puppet::Provider::Confine::Feature.new("myfeature")
+ @confine = Puppet::Confine::Feature.new("myfeature")
@confine.label = "eh"
end
@@ -44,14 +44,14 @@ describe Puppet::Provider::Confine::Feature do
it "should summarize multiple instances by returning a flattened array of all missing features" do
confines = []
- confines << Puppet::Provider::Confine::Feature.new(%w{one two})
- confines << Puppet::Provider::Confine::Feature.new(%w{two})
- confines << Puppet::Provider::Confine::Feature.new(%w{three four})
+ confines << Puppet::Confine::Feature.new(%w{one two})
+ confines << Puppet::Confine::Feature.new(%w{two})
+ confines << Puppet::Confine::Feature.new(%w{three four})
features = mock 'feature'
features.stub_everything
Puppet.stubs(:features).returns features
- Puppet::Provider::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort
+ Puppet::Confine::Feature.summarize(confines).sort.should == %w{one two three four}.sort
end
end
diff --git a/spec/unit/provider/confine/true_spec.rb b/spec/unit/confine/true_spec.rb
index e869817f0..d7484d59c 100755
--- a/spec/unit/provider/confine/true_spec.rb
+++ b/spec/unit/confine/true_spec.rb
@@ -1,20 +1,20 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confine/true'
+require 'puppet/confine/true'
-describe Puppet::Provider::Confine::True do
+describe Puppet::Confine::True do
it "should be named :true" do
- Puppet::Provider::Confine::True.name.should == :true
+ Puppet::Confine::True.name.should == :true
end
it "should require a value" do
- lambda { Puppet::Provider::Confine::True.new }.should raise_error(ArgumentError)
+ lambda { Puppet::Confine::True.new }.should raise_error(ArgumentError)
end
describe "when testing values" do
before do
- @confine = Puppet::Provider::Confine::True.new("foo")
+ @confine = Puppet::Confine::True.new("foo")
@confine.label = "eh"
end
@@ -37,7 +37,7 @@ describe Puppet::Provider::Confine::True do
end
it "should produce the number of false values when asked for a summary" do
- @confine = Puppet::Provider::Confine::True.new %w{one two three four}
+ @confine = Puppet::Confine::True.new %w{one two three four}
@confine.expects(:pass?).times(4).returns(true).returns(false).returns(true).returns(false)
@confine.summary.should == 2
end
@@ -47,6 +47,6 @@ describe Puppet::Provider::Confine::True do
c2 = mock '2', :summary => 2
c3 = mock '3', :summary => 3
- Puppet::Provider::Confine::True.summarize([c1, c2, c3]).should == 6
+ Puppet::Confine::True.summarize([c1, c2, c3]).should == 6
end
end
diff --git a/spec/unit/provider/confine/variable_spec.rb b/spec/unit/confine/variable_spec.rb
index d06705c6f..be45e3bb1 100755
--- a/spec/unit/provider/confine/variable_spec.rb
+++ b/spec/unit/confine/variable_spec.rb
@@ -1,28 +1,28 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confine/variable'
+require 'puppet/confine/variable'
-describe Puppet::Provider::Confine::Variable do
+describe Puppet::Confine::Variable do
it "should be named :variable" do
- Puppet::Provider::Confine::Variable.name.should == :variable
+ Puppet::Confine::Variable.name.should == :variable
end
it "should require a value" do
- lambda { Puppet::Provider::Confine::Variable.new }.should raise_error(ArgumentError)
+ lambda { Puppet::Confine::Variable.new }.should raise_error(ArgumentError)
end
it "should always convert values to an array" do
- Puppet::Provider::Confine::Variable.new("/some/file").values.should be_instance_of(Array)
+ Puppet::Confine::Variable.new("/some/file").values.should be_instance_of(Array)
end
it "should have an accessor for its name" do
- Puppet::Provider::Confine::Variable.new(:bar).should respond_to(:name)
+ Puppet::Confine::Variable.new(:bar).should respond_to(:name)
end
describe "when testing values" do
before do
- @confine = Puppet::Provider::Confine::Variable.new("foo")
+ @confine = Puppet::Confine::Variable.new("foo")
@confine.name = :myvar
end
@@ -63,7 +63,7 @@ describe Puppet::Provider::Confine::Variable do
end
it "should produce a message that the fact value is not correct" do
- @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee})
+ @confine = Puppet::Confine::Variable.new(%w{bar bee})
@confine.name = "eh"
message = @confine.message("value")
message.should be_include("facter")
@@ -71,7 +71,7 @@ describe Puppet::Provider::Confine::Variable do
end
it "should be valid if the test value matches any of the provided values" do
- @confine = Puppet::Provider::Confine::Variable.new(%w{bar bee})
+ @confine = Puppet::Confine::Variable.new(%w{bar bee})
@confine.expects(:test_value).returns "bee"
@confine.should be_valid
end
@@ -79,28 +79,28 @@ describe Puppet::Provider::Confine::Variable do
describe "when summarizing multiple instances" do
it "should return a hash of failing variables and their values" do
- c1 = Puppet::Provider::Confine::Variable.new("one")
+ c1 = Puppet::Confine::Variable.new("one")
c1.name = "uno"
c1.expects(:valid?).returns false
- c2 = Puppet::Provider::Confine::Variable.new("two")
+ c2 = Puppet::Confine::Variable.new("two")
c2.name = "dos"
c2.expects(:valid?).returns true
- c3 = Puppet::Provider::Confine::Variable.new("three")
+ c3 = Puppet::Confine::Variable.new("three")
c3.name = "tres"
c3.expects(:valid?).returns false
- Puppet::Provider::Confine::Variable.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}}
+ Puppet::Confine::Variable.summarize([c1, c2, c3]).should == {"uno" => %w{one}, "tres" => %w{three}}
end
it "should combine the values of multiple confines with the same fact" do
- c1 = Puppet::Provider::Confine::Variable.new("one")
+ c1 = Puppet::Confine::Variable.new("one")
c1.name = "uno"
c1.expects(:valid?).returns false
- c2 = Puppet::Provider::Confine::Variable.new("two")
+ c2 = Puppet::Confine::Variable.new("two")
c2.name = "uno"
c2.expects(:valid?).returns false
- Puppet::Provider::Confine::Variable.summarize([c1, c2]).should == {"uno" => %w{one two}}
+ Puppet::Confine::Variable.summarize([c1, c2]).should == {"uno" => %w{one two}}
end
end
end
diff --git a/spec/unit/provider/confine_collection_spec.rb b/spec/unit/confine_collection_spec.rb
index 31a778fd7..958f69197 100755
--- a/spec/unit/provider/confine_collection_spec.rb
+++ b/spec/unit/confine_collection_spec.rb
@@ -1,72 +1,72 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confine_collection'
+require 'puppet/confine_collection'
-describe Puppet::Provider::ConfineCollection do
+describe Puppet::ConfineCollection do
it "should be able to add confines" do
- Puppet::Provider::ConfineCollection.new("label").should respond_to(:confine)
+ Puppet::ConfineCollection.new("label").should respond_to(:confine)
end
it "should require a label at initialization" do
- lambda { Puppet::Provider::ConfineCollection.new }.should raise_error(ArgumentError)
+ lambda { Puppet::ConfineCollection.new }.should raise_error(ArgumentError)
end
it "should make its label available" do
- Puppet::Provider::ConfineCollection.new("mylabel").label.should == "mylabel"
+ Puppet::ConfineCollection.new("mylabel").label.should == "mylabel"
end
describe "when creating confine instances" do
it "should create an instance of the named test with the provided values" do
test_class = mock 'test_class'
test_class.expects(:new).with(%w{my values}).returns(stub('confine', :label= => nil))
- Puppet::Provider::Confine.expects(:test).with(:foo).returns test_class
+ Puppet::Confine.expects(:test).with(:foo).returns test_class
- Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values}
+ Puppet::ConfineCollection.new("label").confine :foo => %w{my values}
end
it "should copy its label to the confine instance" do
confine = mock 'confine'
test_class = mock 'test_class'
test_class.expects(:new).returns confine
- Puppet::Provider::Confine.expects(:test).returns test_class
+ Puppet::Confine.expects(:test).returns test_class
confine.expects(:label=).with("label")
- Puppet::Provider::ConfineCollection.new("label").confine :foo => %w{my values}
+ Puppet::ConfineCollection.new("label").confine :foo => %w{my values}
end
describe "and the test cannot be found" do
it "should create a Facter test with the provided values and set the name to the test name" do
- confine = Puppet::Provider::Confine.test(:variable).new(%w{my values})
+ confine = Puppet::Confine.test(:variable).new(%w{my values})
confine.expects(:name=).with(:foo)
confine.class.expects(:new).with(%w{my values}).returns confine
- Puppet::Provider::ConfineCollection.new("label").confine(:foo => %w{my values})
+ Puppet::ConfineCollection.new("label").confine(:foo => %w{my values})
end
end
describe "and the 'for_binary' option was provided" do
it "should mark the test as a binary confine" do
- confine = Puppet::Provider::Confine.test(:exists).new(:bar)
+ confine = Puppet::Confine.test(:exists).new(:bar)
confine.expects(:for_binary=).with true
- Puppet::Provider::Confine.test(:exists).expects(:new).with(:bar).returns confine
- Puppet::Provider::ConfineCollection.new("label").confine :exists => :bar, :for_binary => true
+ Puppet::Confine.test(:exists).expects(:new).with(:bar).returns confine
+ Puppet::ConfineCollection.new("label").confine :exists => :bar, :for_binary => true
end
end
end
it "should be valid if no confines are present" do
- Puppet::Provider::ConfineCollection.new("label").should be_valid
+ Puppet::ConfineCollection.new("label").should be_valid
end
it "should be valid if all confines pass" do
c1 = stub 'c1', :valid? => true, :label= => nil
c2 = stub 'c2', :valid? => true, :label= => nil
- Puppet::Provider::Confine.test(:true).expects(:new).returns(c1)
- Puppet::Provider::Confine.test(:false).expects(:new).returns(c2)
+ Puppet::Confine.test(:true).expects(:new).returns(c1)
+ Puppet::Confine.test(:false).expects(:new).returns(c2)
- confiner = Puppet::Provider::ConfineCollection.new("label")
+ confiner = Puppet::ConfineCollection.new("label")
confiner.confine :true => :bar, :false => :bee
confiner.should be_valid
@@ -76,10 +76,10 @@ describe Puppet::Provider::ConfineCollection do
c1 = stub 'c1', :valid? => true, :label= => nil
c2 = stub 'c2', :valid? => false, :label= => nil
- Puppet::Provider::Confine.test(:true).expects(:new).returns(c1)
- Puppet::Provider::Confine.test(:false).expects(:new).returns(c2)
+ Puppet::Confine.test(:true).expects(:new).returns(c1)
+ Puppet::Confine.test(:false).expects(:new).returns(c2)
- confiner = Puppet::Provider::ConfineCollection.new("label")
+ confiner = Puppet::ConfineCollection.new("label")
confiner.confine :true => :bar, :false => :bee
confiner.should_not be_valid
@@ -87,7 +87,7 @@ describe Puppet::Provider::ConfineCollection do
describe "when providing a summary" do
before do
- @confiner = Puppet::Provider::ConfineCollection.new("label")
+ @confiner = Puppet::ConfineCollection.new("label")
end
it "should return a hash" do
@@ -100,32 +100,32 @@ describe Puppet::Provider::ConfineCollection do
it "should add each test type's summary to the hash" do
@confiner.confine :true => :bar, :false => :bee
- Puppet::Provider::Confine.test(:true).expects(:summarize).returns :tsumm
- Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+ Puppet::Confine.test(:true).expects(:summarize).returns :tsumm
+ Puppet::Confine.test(:false).expects(:summarize).returns :fsumm
@confiner.summary.should == {:true => :tsumm, :false => :fsumm}
end
it "should not include tests that return 0" do
@confiner.confine :true => :bar, :false => :bee
- Puppet::Provider::Confine.test(:true).expects(:summarize).returns 0
- Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+ Puppet::Confine.test(:true).expects(:summarize).returns 0
+ Puppet::Confine.test(:false).expects(:summarize).returns :fsumm
@confiner.summary.should == {:false => :fsumm}
end
it "should not include tests that return empty arrays" do
@confiner.confine :true => :bar, :false => :bee
- Puppet::Provider::Confine.test(:true).expects(:summarize).returns []
- Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+ Puppet::Confine.test(:true).expects(:summarize).returns []
+ Puppet::Confine.test(:false).expects(:summarize).returns :fsumm
@confiner.summary.should == {:false => :fsumm}
end
it "should not include tests that return empty hashes" do
@confiner.confine :true => :bar, :false => :bee
- Puppet::Provider::Confine.test(:true).expects(:summarize).returns({})
- Puppet::Provider::Confine.test(:false).expects(:summarize).returns :fsumm
+ Puppet::Confine.test(:true).expects(:summarize).returns({})
+ Puppet::Confine.test(:false).expects(:summarize).returns :fsumm
@confiner.summary.should == {:false => :fsumm}
end
diff --git a/spec/unit/provider/confine_spec.rb b/spec/unit/confine_spec.rb
index 38dfd8c3f..06c10abd7 100755
--- a/spec/unit/provider/confine_spec.rb
+++ b/spec/unit/confine_spec.rb
@@ -1,40 +1,40 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confine'
+require 'puppet/confine'
-describe Puppet::Provider::Confine do
+describe Puppet::Confine do
it "should require a value" do
- lambda { Puppet::Provider::Confine.new }.should raise_error(ArgumentError)
+ lambda { Puppet::Confine.new }.should raise_error(ArgumentError)
end
it "should always convert values to an array" do
- Puppet::Provider::Confine.new("/some/file").values.should be_instance_of(Array)
+ Puppet::Confine.new("/some/file").values.should be_instance_of(Array)
end
it "should have a 'true' test" do
- Puppet::Provider::Confine.test(:true).should be_instance_of(Class)
+ Puppet::Confine.test(:true).should be_instance_of(Class)
end
it "should have a 'false' test" do
- Puppet::Provider::Confine.test(:false).should be_instance_of(Class)
+ Puppet::Confine.test(:false).should be_instance_of(Class)
end
it "should have a 'feature' test" do
- Puppet::Provider::Confine.test(:feature).should be_instance_of(Class)
+ Puppet::Confine.test(:feature).should be_instance_of(Class)
end
it "should have an 'exists' test" do
- Puppet::Provider::Confine.test(:exists).should be_instance_of(Class)
+ Puppet::Confine.test(:exists).should be_instance_of(Class)
end
it "should have a 'variable' test" do
- Puppet::Provider::Confine.test(:variable).should be_instance_of(Class)
+ Puppet::Confine.test(:variable).should be_instance_of(Class)
end
describe "when testing all values" do
before do
- @confine = Puppet::Provider::Confine.new(%w{a b c})
+ @confine = Puppet::Confine.new(%w{a b c})
@confine.label = "foo"
end
@@ -64,7 +64,7 @@ describe Puppet::Provider::Confine do
end
describe "when testing the result of the values" do
- before { @confine = Puppet::Provider::Confine.new(%w{a b c d}) }
+ before { @confine = Puppet::Confine.new(%w{a b c d}) }
it "should return an array with the result of the test for each value" do
@confine.stubs(:pass?).returns true
diff --git a/spec/unit/provider/confiner_spec.rb b/spec/unit/confiner_spec.rb
index 2afdd71a3..2d71054fe 100755
--- a/spec/unit/provider/confiner_spec.rb
+++ b/spec/unit/confiner_spec.rb
@@ -1,12 +1,12 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'puppet/provider/confiner'
+require 'puppet/confiner'
-describe Puppet::Provider::Confiner do
+describe Puppet::Confiner do
before do
@object = Object.new
- @object.extend(Puppet::Provider::Confiner)
+ @object.extend(Puppet::Confiner)
end
it "should have a method for defining confines" do
@@ -29,7 +29,7 @@ describe Puppet::Provider::Confiner do
end
it "should create a new confine collection if one does not exist" do
- Puppet::Provider::ConfineCollection.expects(:new).with("mylabel").returns "mycoll"
+ Puppet::ConfineCollection.expects(:new).with("mylabel").returns "mycoll"
@object.expects(:to_s).returns "mylabel"
@object.confine_collection.should == "mycoll"
end
diff --git a/spec/unit/face/parser_spec.rb b/spec/unit/face/parser_spec.rb
new file mode 100644
index 000000000..a517ae641
--- /dev/null
+++ b/spec/unit/face/parser_spec.rb
@@ -0,0 +1,54 @@
+require 'spec_helper'
+require 'puppet_spec/files'
+
+require 'puppet/face'
+
+describe Puppet::Face[:parser, :current] do
+ include PuppetSpec::Files
+
+ let(:parser) { Puppet::Face[:parser, :current] }
+
+ it "validates the configured site manifest when no files are given" do
+ Puppet[:manifest] = file_containing('site.pp', "{ invalid =>")
+ from_an_interactive_terminal
+
+ expect { parser.validate() }.to exit_with(1)
+ end
+
+ it "validates the given file" do
+ manifest = file_containing('site.pp', "{ invalid =>")
+ from_an_interactive_terminal
+
+ expect { parser.validate(manifest) }.to exit_with(1)
+ end
+
+ it "validates the contents of STDIN when no files given and STDIN is not a tty" do
+ from_a_piped_input_of("{ invalid =>")
+
+ expect { parser.validate() }.to exit_with(1)
+ end
+
+ it "runs error free when there are no validation errors" do
+ manifest = file_containing('site.pp', "notify { valid: }")
+ from_an_interactive_terminal
+
+ parser.validate(manifest)
+ end
+
+ it "reports missing files" do
+ from_an_interactive_terminal
+
+ expect do
+ parser.validate("missing.pp")
+ end.to raise_error(Puppet::Error, /One or more file\(s\) specified did not exist.*missing\.pp/m)
+ end
+
+ def from_an_interactive_terminal
+ STDIN.stubs(:tty?).returns(true)
+ end
+
+ def from_a_piped_input_of(contents)
+ STDIN.stubs(:tty?).returns(false)
+ STDIN.stubs(:read).returns(contents)
+ end
+end
diff --git a/spec/unit/file_bucket/dipper_spec.rb b/spec/unit/file_bucket/dipper_spec.rb
index 397cb9219..83e914851 100755
--- a/spec/unit/file_bucket/dipper_spec.rb
+++ b/spec/unit/file_bucket/dipper_spec.rb
@@ -45,7 +45,7 @@ describe Puppet::FileBucket::Dipper do
Digest::MD5.hexdigest("my\r\ncontents").should == checksum
@dipper.backup(file).should == checksum
- File.exists?("#{file_bucket}/f/0/d/7/d/4/e/4/f0d7d4e480ad698ed56aeec8b6bd6dea/contents").should == true
+ Puppet::FileSystem::File.exist?("#{file_bucket}/f/0/d/7/d/4/e/4/f0d7d4e480ad698ed56aeec8b6bd6dea/contents").should == true
end
it "should not backup a file that is already in the bucket" do
@@ -123,7 +123,7 @@ describe Puppet::FileBucket::Dipper do
klass.any_instance.expects(:find).with { |r| request = r }.returns(Puppet::FileBucket::File.new(contents))
dipper.restore(dest, md5).should == md5
- Digest::MD5.hexdigest(IO.binread(dest)).should == md5
+ Digest::MD5.hexdigest(Puppet::FileSystem::File.new(dest).binread).should == md5
request.key.should == "md5/#{md5}"
request.server.should == server
diff --git a/spec/unit/file_serving/base_spec.rb b/spec/unit/file_serving/base_spec.rb
index a172830e8..65168d3a3 100755
--- a/spec/unit/file_serving/base_spec.rb
+++ b/spec/unit/file_serving/base_spec.rb
@@ -42,12 +42,12 @@ describe Puppet::FileServing::Base do
end
it "should allow specification of a path" do
- FileTest.stubs(:exists?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
Puppet::FileServing::Base.new(path, :path => file).path.should == file
end
it "should allow specification of a relative path" do
- FileTest.stubs(:exists?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
Puppet::FileServing::Base.new(path, :relative_path => "my/file").relative_path.should == "my/file"
end
@@ -56,19 +56,22 @@ describe Puppet::FileServing::Base do
end
it "should correctly indicate if the file is present" do
- File.expects(:lstat).with(file).returns(mock("stat"))
+ mock_file = mock(file, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(file).returns mock_file
Puppet::FileServing::Base.new(file).exist?.should be_true
end
it "should correctly indicate if the file is absent" do
- File.expects(:lstat).with(file).raises RuntimeError
+ mock_file = mock(file)
+ Puppet::FileSystem::File.expects(:new).with(file).returns mock_file
+ mock_file.expects(:lstat).raises RuntimeError
Puppet::FileServing::Base.new(file).exist?.should be_false
end
describe "when setting the relative path" do
it "should require that the relative path be unqualified" do
@file = Puppet::FileServing::Base.new(path)
- FileTest.stubs(:exists?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
proc { @file.relative_path = File.expand_path("/qualified/file") }.should raise_error(ArgumentError)
end
end
@@ -102,27 +105,47 @@ describe Puppet::FileServing::Base do
end
end
+ describe "when handling a UNC file path on Windows" do
+ let(:path) { '//server/share/filename' }
+ let(:file) { Puppet::FileServing::Base.new(path) }
+
+ it "should preserve double slashes at the beginning of the path" do
+ Puppet.features.stubs(:microsoft_windows?).returns(true)
+ file.full_path.should == path
+ end
+
+ it "should strip double slashes not at the beginning of the path" do
+ Puppet.features.stubs(:microsoft_windows?).returns(true)
+ file = Puppet::FileServing::Base.new('//server//share//filename')
+ file.full_path.should == path
+ end
+ end
+
+
describe "when stat'ing files" do
let(:path) { File.expand_path('/this/file') }
let(:file) { Puppet::FileServing::Base.new(path) }
+ let(:stat) { stub('stat', :ftype => 'file' ) }
+ let(:stubbed_file) { stub(path, :stat => stat, :lstat => stat)}
it "should stat the file's full path" do
- File.expects(:lstat).with(path).returns stub("stat", :ftype => "file")
+ Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file
file.stat
end
it "should fail if the file does not exist" do
- File.expects(:lstat).with(path).raises(Errno::ENOENT)
+ Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file
+ stubbed_file.expects(:lstat).raises(Errno::ENOENT)
proc { file.stat }.should raise_error(Errno::ENOENT)
end
it "should use :lstat if :links is set to :manage" do
- File.expects(:lstat).with(path).returns stub("stat", :ftype => "file")
+ Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file
file.stat
end
it "should use :stat if :links is set to :follow" do
- File.expects(:stat).with(path).returns stub("stat", :ftype => "file")
+ Puppet::FileSystem::File.expects(:new).with(path).returns stubbed_file
file.links = :follow
file.stat
end
diff --git a/spec/unit/file_serving/configuration_spec.rb b/spec/unit/file_serving/configuration_spec.rb
index 2552cd808..b6999e086 100755
--- a/spec/unit/file_serving/configuration_spec.rb
+++ b/spec/unit/file_serving/configuration_spec.rb
@@ -27,12 +27,12 @@ describe Puppet::FileServing::Configuration do
describe "when initializing" do
it "should work without a configuration file" do
- FileTest.stubs(:exists?).with(@path).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).with(@path).returns(false)
expect { Puppet::FileServing::Configuration.configuration }.to_not raise_error
end
it "should parse the configuration file if present" do
- FileTest.stubs(:exists?).with(@path).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(@path).returns(true)
@parser = mock 'parser'
@parser.expects(:parse).returns({})
Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser)
@@ -47,7 +47,7 @@ describe Puppet::FileServing::Configuration do
describe "when parsing the configuration file" do
before do
- FileTest.stubs(:exists?).with(@path).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(@path).returns(true)
@parser = mock 'parser'
Puppet::FileServing::Configuration::Parser.stubs(:new).returns(@parser)
end
@@ -84,14 +84,14 @@ describe Puppet::FileServing::Configuration do
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
+ Puppet::FileSystem::File.expects(:exist?).returns false # the file doesn't exist
config = Puppet::FileServing::Configuration.configuration
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
+ Puppet::FileSystem::File.expects(:exist?).returns false # the file doesn't exist
modules = stub 'modules', :empty? => true
Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules)
modules.expects(:allow).with('*')
@@ -104,7 +104,7 @@ describe Puppet::FileServing::Configuration do
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
+ Puppet::FileSystem::File.expects(:exist?).returns false # the file doesn't exist
modules = stub 'modules', :empty? => false
Puppet::FileServing::Mount::Modules.stubs(:new).returns(modules)
@@ -119,7 +119,7 @@ describe Puppet::FileServing::Configuration do
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
+ Puppet::FileSystem::File.expects(:exist?).returns true # the file doesn't exist
config = Puppet::FileServing::Configuration.configuration
config.mounted?("modules").should be_true
config.mounted?("plugins").should be_true
diff --git a/spec/unit/file_serving/content_spec.rb b/spec/unit/file_serving/content_spec.rb
index 2169c9b57..2cb159f0a 100755
--- a/spec/unit/file_serving/content_spec.rb
+++ b/spec/unit/file_serving/content_spec.rb
@@ -26,7 +26,8 @@ describe Puppet::FileServing::Content do
content = Puppet::FileServing::Content.new(path)
result = "foo"
- File.stubs(:lstat).returns(stub("stat", :ftype => "file"))
+ stub_file = stub(path, :lstat => stub('stat', :ftype => "file"))
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
File.expects(:read).with(path).never
content.collect
@@ -37,7 +38,8 @@ describe Puppet::FileServing::Content do
content = Puppet::FileServing::Content.new(path)
result = "foo"
- File.stubs(:lstat).returns(stub("stat", :ftype => "directory"))
+ stub_file = stub(path, :lstat => stub('stat', :ftype => "directory"))
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
File.expects(:read).with(path).never
content.collect
@@ -83,7 +85,8 @@ describe Puppet::FileServing::Content, "when returning the contents" do
it "should fail if the file is a symlink and links are set to :manage" do
content.links = :manage
- File.expects(:lstat).with(path).returns stub("stat", :ftype => "symlink")
+ stub_file = stub(path, :lstat => stub("stat", :ftype => "symlink"))
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
proc { content.content }.should raise_error(ArgumentError)
end
@@ -97,14 +100,16 @@ describe Puppet::FileServing::Content, "when returning the contents" do
end
it "should return the contents of the path if the file exists" do
- File.expects(:stat).with(path).returns stub("stat", :ftype => "file")
- IO.expects(:binread).with(path).returns(:mycontent)
+ mocked_file = mock(path, :stat => stub('stat', :ftype => 'file'))
+ Puppet::FileSystem::File.expects(:new).with(path).twice.returns(mocked_file)
+ mocked_file.expects(:binread).returns(:mycontent)
content.content.should == :mycontent
end
it "should cache the returned contents" do
- File.expects(:stat).with(path).returns stub("stat", :ftype => "file")
- IO.expects(:binread).with(path).returns(:mycontent)
+ mocked_file = mock(path, :stat => stub('stat', :ftype => 'file'))
+ Puppet::FileSystem::File.expects(:new).with(path).twice.returns(mocked_file)
+ mocked_file.expects(:binread).returns(:mycontent)
content.content
# The second run would throw a failure if the content weren't being cached.
content.content
diff --git a/spec/unit/file_serving/fileset_spec.rb b/spec/unit/file_serving/fileset_spec.rb
index f6a661843..0e61a2a59 100755
--- a/spec/unit/file_serving/fileset_spec.rb
+++ b/spec/unit/file_serving/fileset_spec.rb
@@ -18,49 +18,58 @@ describe Puppet::FileServing::Fileset do
it "removes a trailing file path separator" do
path_with_separator = "#{somefile}#{File::SEPARATOR}"
- File.stubs(:lstat).with(somefile).returns stub('stat')
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
fileset = Puppet::FileServing::Fileset.new(path_with_separator)
fileset.path.should == somefile
end
it "can be created from the root directory" do
path = File.expand_path(File::SEPARATOR)
- File.stubs(:lstat).with(path).returns stub('stat')
+ stub_file = stub(path, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
fileset = Puppet::FileServing::Fileset.new(path)
fileset.path.should == path
end
it "fails if its path does not exist" do
- File.expects(:lstat).with(somefile).raises(Errno::ENOENT)
+ mock_file = mock(somefile)
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns mock_file
+ mock_file.expects(:lstat).raises(Errno::ENOENT)
expect { Puppet::FileServing::Fileset.new(somefile) }.to raise_error(ArgumentError, "Fileset paths must exist")
end
it "accepts a 'recurse' option" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
set = Puppet::FileServing::Fileset.new(somefile, :recurse => true)
set.recurse.should be_true
end
it "accepts a 'recurselimit' option" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
set = Puppet::FileServing::Fileset.new(somefile, :recurselimit => 3)
set.recurselimit.should == 3
end
it "accepts an 'ignore' option" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
set = Puppet::FileServing::Fileset.new(somefile, :ignore => ".svn")
set.ignore.should == [".svn"]
end
it "accepts a 'links' option" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
set = Puppet::FileServing::Fileset.new(somefile, :links => :manage)
set.links.should == :manage
end
it "accepts a 'checksum_type' option" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
set = Puppet::FileServing::Fileset.new(somefile, :checksum_type => :test)
set.checksum_type.should == :test
end
@@ -70,30 +79,35 @@ describe Puppet::FileServing::Fileset do
end
it "defaults to 'false' for recurse" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
Puppet::FileServing::Fileset.new(somefile).recurse.should == false
end
it "defaults to :infinite for recurselimit" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
Puppet::FileServing::Fileset.new(somefile).recurselimit.should == :infinite
end
it "defaults to an empty ignore list" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
Puppet::FileServing::Fileset.new(somefile).ignore.should == []
end
it "defaults to :manage for links" do
- File.expects(:lstat).with(somefile).returns stub("stat")
+ stub_file = stub(somefile, :lstat => stub('stat'))
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
Puppet::FileServing::Fileset.new(somefile).links.should == :manage
end
describe "using an indirector request" do
let(:values) { { :links => :manage, :ignore => %w{a b}, :recurse => true, :recurselimit => 1234 } }
+ let(:stub_file) { stub(somefile, :lstat => stub('stat')) }
before :each do
- File.stubs(:lstat).returns stub("stat")
+ Puppet::FileSystem::File.expects(:new).with(somefile).returns stub_file
end
[:recurse, :recurselimit, :ignore, :links].each do |option|
@@ -130,7 +144,8 @@ describe Puppet::FileServing::Fileset do
context "when recursing" do
before do
@path = make_absolute("/my/path")
- File.expects(:lstat).with(@path).returns stub("stat", :directory? => true)
+ @stub_file = stub(@path, :lstat => stub('stat', :directory? => true))
+ Puppet::FileSystem::File.stubs(:new).with(@path).returns @stub_file
@fileset = Puppet::FileServing::Fileset.new(@path)
@dirstat = stub 'dirstat', :directory? => true
@@ -138,7 +153,7 @@ describe Puppet::FileServing::Fileset do
end
def mock_dir_structure(path, stat_method = :lstat)
- File.stubs(stat_method).with(path).returns(@dirstat)
+ @stub_file.stubs(stat_method).returns(@dirstat)
Dir.stubs(:entries).with(path).returns(%w{one two .svn CVS})
# Keep track of the files we're stubbing.
@@ -147,11 +162,14 @@ describe Puppet::FileServing::Fileset do
%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)
+ stub_subpath = stub(subpath, stat_method => @dirstat)
+ Puppet::FileSystem::File.stubs(:new).with(subpath).returns stub_subpath
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)
+ subfile_path = File.join(subpath, file)
+ stub_subfile_path = stub(subfile_path, stat_method => @filestat)
+ Puppet::FileSystem::File.stubs(:new).with(subfile_path).returns stub_subfile_path
end
end
end
@@ -165,8 +183,10 @@ describe Puppet::FileServing::Fileset do
MockDirectory = Struct.new(:name, :entries) do
def mock(base_path)
+ extend Mocha::API
path = File.join(base_path, name)
- File.stubs(:lstat).with(path).returns(MockStat.new(path, true))
+ stub_dir = stub(path, :lstat => MockStat.new(path, true))
+ Puppet::FileSystem::File.stubs(:new).with(path).returns stub_dir
Dir.stubs(:entries).with(path).returns(['.', '..'] + entries.map(&:name))
entries.each do |entry|
entry.mock(path)
@@ -176,8 +196,10 @@ describe Puppet::FileServing::Fileset do
MockFile = Struct.new(:name) do
def mock(base_path)
+ extend Mocha::API
path = File.join(base_path, name)
- File.stubs(:lstat).with(path).returns(MockStat.new(path, false))
+ stub_file = stub(path, :lstat => MockStat.new(path, false))
+ Puppet::FileSystem::File.stubs(:new).with(path).returns stub_file
end
end
@@ -239,14 +261,14 @@ describe Puppet::FileServing::Fileset do
@fileset.files.find { |file| file.include?(".svn") or file.include?("CVS") }.should be_nil
end
- it "uses File.stat if :links is set to :follow" do
+ it "uses Puppet::FileSystem::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 "uses File.lstat if :links is set to :manage" do
+ it "uses Puppet::FileSystem::File#lstat if :links is set to :manage" do
mock_dir_structure(@path, :lstat)
@fileset.recurse = true
@fileset.links = :manage
@@ -255,7 +277,9 @@ describe Puppet::FileServing::Fileset do
it "works when paths have regexp significant characters" do
@path = make_absolute("/my/path/rV1x2DafFr0R6tGG+1bbk++++TM")
- File.expects(:lstat).with(@path).returns stub("stat", :directory? => true)
+ stat = stub('dir_stat', :directory? => true)
+ stub_file = stub(@path, :stat => stat, :lstat => stat)
+ Puppet::FileSystem::File.expects(:new).with(@path).twice.returns stub_file
@fileset = Puppet::FileServing::Fileset.new(@path)
mock_dir_structure(@path)
@fileset.recurse = true
@@ -267,9 +291,13 @@ describe Puppet::FileServing::Fileset do
path = make_absolute("/my/path")
stat = stub 'stat', :directory? => true
- File.expects(:lstat).with(path).returns(stat)
- File.expects(:stat).with(path).returns(stat)
- File.expects(:stat).with(File.join(path, "mylink")).raises(Errno::ENOENT)
+ mock_file = mock(path, :lstat => stat, :stat => stat)
+ Puppet::FileSystem::File.expects(:new).with(path).twice.returns mock_file
+
+ link_path = File.join(path, "mylink")
+ mock_link = mock(link_path)
+ Puppet::FileSystem::File.expects(:new).with(link_path).returns mock_link
+ mock_link.expects(:stat).raises(Errno::ENOENT)
Dir.stubs(:entries).with(path).returns(["mylink"])
@@ -284,10 +312,12 @@ describe Puppet::FileServing::Fileset do
context "when merging other filesets" do
before do
@paths = [make_absolute("/first/path"), make_absolute("/second/path"), make_absolute("/third/path")]
- File.stubs(:lstat).returns stub("stat", :directory? => false)
+ stub_file = stub(:lstat => stub('stat', :directory? => false))
+ Puppet::FileSystem::File.stubs(:new).returns stub_file
@filesets = @paths.collect do |path|
- File.stubs(:lstat).with(path).returns stub("stat", :directory? => true)
+ stub_dir = stub(path, :lstat => stub('stat', :directory? => true))
+ Puppet::FileSystem::File.stubs(:new).with(path).returns stub_dir
Puppet::FileServing::Fileset.new(path, :recurse => true)
end
diff --git a/spec/unit/file_serving/metadata_spec.rb b/spec/unit/file_serving/metadata_spec.rb
index 28d48e4ec..8b74d4b7e 100755
--- a/spec/unit/file_serving/metadata_spec.rb
+++ b/spec/unit/file_serving/metadata_spec.rb
@@ -3,6 +3,21 @@ require 'spec_helper'
require 'puppet/file_serving/metadata'
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ FILE_METADATA_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/file_metadata.json')))
+
+ describe "catalog schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, FILE_METADATA_SCHEMA)
+ end
+ end
+
+ def validate_json_for_file_metadata(file_metadata)
+ JSON::Validator.validate!(FILE_METADATA_SCHEMA, file_metadata.to_pson)
+ end
+end
+
describe Puppet::FileServing::Metadata do
let(:foobar) { File.expand_path('/foo/bar') }
@@ -84,7 +99,6 @@ describe Puppet::FileServing::Metadata do
it "should pass the checksum in the hash verbatim as the checksum's value" do
metadata.to_pson_data_hash['data']['checksum']['value'].should == metadata.checksum
end
-
end
end
@@ -139,6 +153,10 @@ describe Puppet::FileServing::Metadata do
metadata.checksum.should == "{mtime}#{@time}"
end
end
+
+ it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do
+ validate_json_for_file_metadata(metadata)
+ end
end
describe "when managing directories" do
@@ -160,25 +178,45 @@ describe Puppet::FileServing::Metadata do
metadata.collect
metadata.checksum.should == "{ctime}#{time}"
end
+
+ it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do
+ metadata.collect
+ validate_json_for_file_metadata(metadata)
+ end
end
+ end
+ end
- describe "when managing links", :unless => Puppet.features.microsoft_windows? do
+ shared_examples_for "metadata collector symlinks" do
+
+ let(:metadata) do
+ data = described_class.new(path)
+ data.collect
+ data
+ end
+
+ describe "when collecting attributes" do
+ describe "when managing links" do
# 'path' is a link that points to 'target'
let(:path) { tmpfile('file_serving_metadata_link') }
let(:target) { tmpfile('file_serving_metadata_target') }
let(:checksum) { Digest::MD5.hexdigest("some content\n") }
- let(:fmode) { File.lstat(path).mode & 0777 }
+ let(:fmode) { Puppet::FileSystem::File.new(path).lstat.mode & 0777 }
before :each do
File.open(target, "wb") {|f| f.print("some content\n")}
set_mode(0644, target)
- FileUtils.symlink(target, path)
+ Puppet::FileSystem::File.new(target).symlink(path)
end
it "should read links instead of returning their checksums" do
metadata.destination.should == target
end
+
+ it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do
+ validate_json_for_file_metadata(metadata)
+ end
end
end
@@ -209,6 +247,10 @@ describe Puppet::FileServing::Metadata do
proc { metadata.collect}.should raise_error(Errno::ENOENT)
end
+
+ it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do
+ validate_json_for_file_metadata(metadata)
+ end
end
end
@@ -222,6 +264,7 @@ describe Puppet::FileServing::Metadata do
end
it_should_behave_like "metadata collector"
+ it_should_behave_like "metadata collector symlinks"
def set_mode(mode, path)
File.chmod(mode, path)
@@ -239,6 +282,7 @@ describe Puppet::FileServing::Metadata do
end
it_should_behave_like "metadata collector"
+ it_should_behave_like "metadata collector symlinks" if Puppet.features.manages_symlinks?
describe "if ACL metadata cannot be collected" do
let(:path) { tmpdir('file_serving_metadata_acl') }
@@ -274,16 +318,24 @@ describe Puppet::FileServing::Metadata do
end
-describe Puppet::FileServing::Metadata, " when pointing to a link", :unless => Puppet.features.microsoft_windows? do
+describe Puppet::FileServing::Metadata, " when pointing to a link", :if => Puppet.features.manages_symlinks? do
describe "when links are managed" do
before do
- @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :manage)
- File.expects(:lstat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755)
- File.expects(:readlink).with("/base/path/my/file").returns "/some/other/path"
-
+ path = "/base/path/my/file"
+ @file = Puppet::FileServing::Metadata.new(path, :links => :manage)
+ stat = stub("stat", :uid => 1, :gid => 2, :ftype => "link", :mode => 0755)
+ stub_file = stub(:readlink => "/some/other/path", :lstat => stat)
+ Puppet::FileSystem::File.expects(:new).with(path).at_least_once.returns stub_file
@checksum = Digest::MD5.hexdigest("some content\n") # Remove these when :managed links are no longer checksumed.
@file.stubs(:md5_file).returns(@checksum) #
+
+ if Puppet.features.microsoft_windows?
+ win_stat = stub('win_stat', :owner => 'snarf', :group => 'thundercats',
+ :ftype => 'link', :mode => 0755)
+ Puppet::FileServing::Metadata::WindowsStat.stubs(:new).returns win_stat
+ end
end
+
it "should store the destination of the link in :destination if links are :manage" do
@file.collect
@file.destination.should == "/some/other/path"
@@ -301,9 +353,19 @@ describe Puppet::FileServing::Metadata, " when pointing to a link", :unless => P
describe "when links are followed" do
before do
- @file = Puppet::FileServing::Metadata.new("/base/path/my/file", :links => :follow)
- File.expects(:stat).with("/base/path/my/file").returns stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755)
- File.expects(:readlink).with("/base/path/my/file").never
+ path = "/base/path/my/file"
+ @file = Puppet::FileServing::Metadata.new(path, :links => :follow)
+ stat = stub("stat", :uid => 1, :gid => 2, :ftype => "file", :mode => 0755)
+ mocked_file = mock(path, :stat => stat)
+ Puppet::FileSystem::File.expects(:new).with(path).at_least_once.returns mocked_file
+ mocked_file.expects(:readlink).never
+
+ if Puppet.features.microsoft_windows?
+ win_stat = stub('win_stat', :owner => 'snarf', :group => 'thundercats',
+ :ftype => 'file', :mode => 0755)
+ Puppet::FileServing::Metadata::WindowsStat.stubs(:new).returns win_stat
+ end
+
@checksum = Digest::MD5.hexdigest("some content\n")
@file.stubs(:md5_file).returns(@checksum)
end
diff --git a/spec/unit/file_serving/mount/file_spec.rb b/spec/unit/file_serving/mount/file_spec.rb
index 59394be46..5821f3f98 100755
--- a/spec/unit/file_serving/mount/file_spec.rb
+++ b/spec/unit/file_serving/mount/file_spec.rb
@@ -85,7 +85,7 @@ describe Puppet::FileServing::Mount::File do
include FileServingMountTesting
before do
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
FileTest.stubs(:directory?).returns(true)
FileTest.stubs(:readable?).returns(true)
@mount = Puppet::FileServing::Mount::File.new("test")
@@ -95,12 +95,12 @@ describe Puppet::FileServing::Mount::File do
end
it "should return nil if the file is absent" do
- FileTest.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
@mount.complete_path("/my/path", nil).should be_nil
end
it "should write a log message if the file is absent" do
- FileTest.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
Puppet.expects(:info).with("File does not exist or is not accessible: /mount/my/path")
@@ -108,12 +108,12 @@ describe Puppet::FileServing::Mount::File do
end
it "should return the file path if the file is present" do
- FileTest.stubs(:exist?).with("/my/path").returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with("/my/path").returns(true)
@mount.complete_path("/my/path", nil).should == "/mount/my/path"
end
it "should treat a nil file name as the path to the mount itself" do
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
@mount.complete_path(nil, nil).should == "/mount"
end
@@ -141,7 +141,7 @@ describe Puppet::FileServing::Mount::File do
include FileServingMountTesting
before do
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
FileTest.stubs(:directory?).returns(true)
FileTest.stubs(:readable?).returns(true)
@mount = Puppet::FileServing::Mount::File.new("test")
@@ -153,7 +153,7 @@ describe Puppet::FileServing::Mount::File do
end
it "should return the results of the complete file path" do
- FileTest.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
@mount.expects(:complete_path).with("/my/path", "foo").returns "eh"
@mount.find("/my/path", @request).should == "eh"
end
@@ -163,7 +163,7 @@ describe Puppet::FileServing::Mount::File do
include FileServingMountTesting
before do
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
FileTest.stubs(:directory?).returns(true)
FileTest.stubs(:readable?).returns(true)
@mount = Puppet::FileServing::Mount::File.new("test")
@@ -175,13 +175,13 @@ describe Puppet::FileServing::Mount::File do
end
it "should return the results of the complete file path as an array" do
- FileTest.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
@mount.expects(:complete_path).with("/my/path", "foo").returns "eh"
@mount.search("/my/path", @request).should == ["eh"]
end
it "should return nil if the complete path is nil" do
- FileTest.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
@mount.expects(:complete_path).with("/my/path", "foo").returns nil
@mount.search("/my/path", @request).should be_nil
end
diff --git a/spec/unit/file_serving/mount/pluginfacts_spec.rb b/spec/unit/file_serving/mount/pluginfacts_spec.rb
new file mode 100755
index 000000000..a54e45e4c
--- /dev/null
+++ b/spec/unit/file_serving/mount/pluginfacts_spec.rb
@@ -0,0 +1,73 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/file_serving/mount/pluginfacts'
+
+describe Puppet::FileServing::Mount::PluginFacts do
+ before do
+ @mount = Puppet::FileServing::Mount::PluginFacts.new("pluginfacts")
+
+ @environment = stub 'environment', :module => nil
+ @options = { :recurse => true }
+ @request = stub 'request', :environment => @environment, :options => @options
+ end
+
+ describe "when finding files" do
+ it "should use the provided environment to find the modules" do
+ @environment.expects(:modules).returns []
+
+ @mount.find("foo", @request)
+ end
+
+ it "should return nil if no module can be found with a matching plugin" do
+ mod = mock 'module'
+ mod.stubs(:pluginfact).with("foo/bar").returns nil
+
+ @environment.stubs(:modules).returns [mod]
+ @mount.find("foo/bar", @request).should be_nil
+ end
+
+ it "should return the file path from the module" do
+ mod = mock 'module'
+ mod.stubs(:pluginfact).with("foo/bar").returns "eh"
+
+ @environment.stubs(:modules).returns [mod]
+ @mount.find("foo/bar", @request).should == "eh"
+ end
+ end
+
+ describe "when searching for files" do
+ it "should use the node's environment to find the modules" do
+ @environment.expects(:modules).at_least_once.returns []
+ @environment.stubs(:modulepath).returns ["/tmp/modules"]
+
+ @mount.search("foo", @request)
+ end
+
+ it "should return modulepath if no modules can be found that have plugins" do
+ mod = mock 'module'
+ mod.stubs(:pluginfacts?).returns false
+
+ @environment.stubs(:modules).returns []
+ @environment.stubs(:modulepath).returns ["/"]
+ @options.expects(:[]=).with(:recurse, false)
+ @mount.search("foo/bar", @request).should == ["/"]
+ end
+
+ it "should return nil if no modules can be found that have plugins and modulepath is invalid" do
+ mod = mock 'module'
+ mod.stubs(:pluginfacts?).returns false
+
+ @environment.stubs(:modules).returns []
+ @environment.stubs(:modulepath).returns []
+ @mount.search("foo/bar", @request).should be_nil
+ end
+
+ it "should return the plugin paths for each module that has plugins" do
+ one = stub 'module', :pluginfacts? => true, :plugin_fact_directory => "/one"
+ two = stub 'module', :pluginfacts? => true, :plugin_fact_directory => "/two"
+
+ @environment.stubs(:modules).returns [one, two]
+ @mount.search("foo/bar", @request).should == %w{/one /two}
+ end
+ end
+end
diff --git a/spec/unit/file_system/file_spec.rb b/spec/unit/file_system/file_spec.rb
new file mode 100644
index 000000000..564d4096c
--- /dev/null
+++ b/spec/unit/file_system/file_spec.rb
@@ -0,0 +1,486 @@
+require 'spec_helper'
+require 'puppet/file_system'
+require 'puppet/util/platform'
+
+describe Puppet::FileSystem::File do
+ include PuppetSpec::Files
+
+ context "#exclusive_open" do
+ it "opens ands allows updating of an existing file" do
+ file = Puppet::FileSystem::File.new(file_containing("file_to_update", "the contents"))
+
+ file.exclusive_open(0660, 'r+') do |fh|
+ old = fh.read
+ fh.truncate(0)
+ fh.rewind
+ fh.write("updated #{old}")
+ end
+
+ expect(file.read).to eq("updated the contents")
+ end
+
+ it "opens, creates ands allows updating of a new file" do
+ file = Puppet::FileSystem::File.new(tmpfile("file_to_update"))
+
+ file.exclusive_open(0660, 'w') do |fh|
+ fh.write("updated new file")
+ end
+
+ expect(file.read).to eq("updated new file")
+ end
+
+ it "excludes other processes from updating at the same time", :unless => Puppet::Util::Platform.windows? do
+ file = Puppet::FileSystem::File.new(file_containing("file_to_update", "0"))
+
+ increment_counter_in_multiple_processes(file, 5, 'r+')
+
+ expect(file.read).to eq("5")
+ end
+
+ it "excludes other processes from updating at the same time even when creating the file", :unless => Puppet::Util::Platform.windows? do
+ file = Puppet::FileSystem::File.new(tmpfile("file_to_update"))
+
+ increment_counter_in_multiple_processes(file, 5, 'a+')
+
+ expect(file.read).to eq("5")
+ end
+
+ it "times out if the lock cannot be aquired in a specified amount of time", :unless => Puppet::Util::Platform.windows? do
+ file = tmpfile("file_to_update")
+
+ child = spawn_process_that_locks(file)
+
+ expect do
+ Puppet::FileSystem::File.new(file).exclusive_open(0666, 'a', 0.1) do |f|
+ end
+ end.to raise_error(Timeout::Error)
+
+ Process.kill(9, child)
+ end
+
+ def spawn_process_that_locks(file)
+ read, write = IO.pipe
+
+ child = Kernel.fork do
+ read.close
+ Puppet::FileSystem::File.new(file).exclusive_open(0666, 'a') do |fh|
+ write.write(true)
+ write.close
+ sleep 10
+ end
+ end
+
+ write.close
+ read.read
+ read.close
+
+ child
+ end
+
+ def increment_counter_in_multiple_processes(file, num_procs, options)
+ children = []
+ 5.times do |number|
+ children << Kernel.fork do
+ file.exclusive_open(0660, options) do |fh|
+ fh.rewind
+ contents = (fh.read || 0).to_i
+ fh.truncate(0)
+ fh.rewind
+ fh.write((contents + 1).to_s)
+ end
+ exit(0)
+ end
+ end
+
+ children.each { |pid| Process.wait(pid) }
+ end
+ end
+
+ describe "symlink",
+ :if => ! Puppet.features.manages_symlinks? &&
+ Puppet.features.microsoft_windows? do
+
+ let (:file) { Puppet::FileSystem::File.new(tmpfile("somefile")) }
+ let (:missing_file) { Puppet::FileSystem::File.new(tmpfile("missingfile")) }
+ let (:expected_msg) { "This version of Windows does not support symlinks. Windows Vista / 2008 or higher is required." }
+
+ before :each do
+ FileUtils.touch(file.path)
+ end
+
+ it "should raise an error when trying to create a symlink" do
+ expect { file.symlink('foo') }.to raise_error(Puppet::Util::Windows::Error)
+ end
+
+ it "should return false when trying to check if a path is a symlink" do
+ file.symlink?.should be_false
+ end
+
+ it "should raise an error when trying to read a symlink" do
+ expect { file.readlink }.to raise_error(Puppet::Util::Windows::Error)
+ end
+
+ it "should return a File::Stat instance when calling stat on an existing file" do
+ file.stat.should be_instance_of(File::Stat)
+ end
+
+ it "should raise Errno::ENOENT when calling stat on a missing file" do
+ expect { missing_file.stat }.to raise_error(Errno::ENOENT)
+ end
+
+ it "should fall back to stat when trying to lstat a file" do
+ Puppet::Util::Windows::File.expects(:stat).with(file.path)
+
+ file.lstat
+ end
+ end
+
+ describe "symlink", :if => Puppet.features.manages_symlinks? do
+
+ let (:file) { Puppet::FileSystem::File.new(tmpfile("somefile")) }
+ let (:missing_file) { Puppet::FileSystem::File.new(tmpfile("missingfile")) }
+ let (:dir) { Puppet::FileSystem::File.new(tmpdir("somedir")) }
+
+ before :each do
+ FileUtils.touch(file.path)
+ end
+
+ it "should return true for exist? on a present file" do
+ file.exist?.should be_true
+ Puppet::FileSystem::File.exist?(file.path).should be_true
+ end
+
+ it "should return false for exist? on a non-existant file" do
+ missing_file.exist?.should be_false
+ Puppet::FileSystem::File.exist?(missing_file.path).should be_false
+ end
+
+ it "should return true for exist? on a present directory" do
+ dir.exist?.should be_true
+ Puppet::FileSystem::File.exist?(dir.path).should be_true
+ end
+
+ it "should return false for exist? on a dangling symlink" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ missing_file.symlink(symlink.path)
+
+ missing_file.exist?.should be_false
+ symlink.exist?.should be_false
+ end
+
+ it "should return true for exist? on valid symlinks" do
+ [file, dir].each do |target|
+ symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
+ target.symlink(symlink.path)
+
+ target.exist?.should be_true
+ symlink.exist?.should be_true
+ end
+ end
+
+ it "should not create a symlink when the :noop option is specified" do
+ [file, dir].each do |target|
+ symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
+ target.symlink(symlink.path, { :noop => true })
+
+ target.exist?.should be_true
+ symlink.exist?.should be_false
+ end
+ end
+
+ it "should raise Errno::EEXIST if trying to create a file / directory symlink when the symlink path already exists as a file" do
+ existing_file = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_link"))
+ FileUtils.touch(existing_file.path)
+
+ [file, dir].each do |target|
+ expect { target.symlink(existing_file.path) }.to raise_error(Errno::EEXIST)
+
+ existing_file.exist?.should be_true
+ existing_file.symlink?.should be_false
+ end
+ end
+
+ it "should silently fail if trying to create a file / directory symlink when the symlink path already exists as a directory" do
+ existing_dir = Puppet::FileSystem::File.new(tmpdir("#{file.path.basename.to_s}_dir"))
+
+ [file, dir].each do |target|
+ target.symlink(existing_dir.path).should == 0
+
+ existing_dir.exist?.should be_true
+ File.directory?(existing_dir.path).should be_true
+ existing_dir.symlink?.should be_false
+ end
+ end
+
+ it "should silently fail to modify an existing directory symlink to reference a new file or directory" do
+ [file, dir].each do |target|
+ existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_dir"))
+ symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_dir.path.basename.to_s}_link"))
+ existing_dir.symlink(symlink.path)
+
+ symlink.readlink.should == existing_dir.path.to_s
+
+ # now try to point it at the new target, no error raised, but file system unchanged
+ target.symlink(symlink.path).should == 0
+ symlink.readlink.should == existing_dir.path.to_s
+ end
+ end
+
+ it "should raise Errno::EEXIST if trying to modify a file symlink to reference a new file or directory" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_link"))
+ file_2 = Puppet::FileSystem::File.new(tmpfile("#{file.path.basename.to_s}_2"))
+ FileUtils.touch(file_2.path)
+ # symlink -> file_2
+ file_2.symlink(symlink.path)
+
+ [file, dir].each do |target|
+ expect { target.symlink(symlink.path) }.to raise_error(Errno::EEXIST)
+ symlink.readlink.should == file_2.path.to_s
+ end
+ end
+
+ it "should delete the existing file when creating a file / directory symlink with :force when the symlink path exists as a file" do
+ [file, dir].each do |target|
+ existing_file = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_existing"))
+ FileUtils.touch(existing_file.path)
+ existing_file.symlink?.should be_false
+
+ target.symlink(existing_file.path, { :force => true })
+
+ existing_file.symlink?.should be_true
+ existing_file.readlink.should == target.path.to_s
+ end
+ end
+
+ it "should modify an existing file symlink when using :force to reference a new file or directory" do
+ [file, dir].each do |target|
+ existing_file = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_existing"))
+ FileUtils.touch(existing_file.path)
+ existing_symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_file.path.basename.to_s}_link"))
+ existing_file.symlink(existing_symlink.path)
+
+ existing_symlink.readlink.should == existing_file.path.to_s
+
+ target.symlink(existing_symlink.path, { :force => true })
+
+ existing_symlink.readlink.should == target.path.to_s
+ end
+ end
+
+ it "should silently fail if trying to overwrite an existing directory with a new symlink when using :force to reference a file or directory" do
+ [file, dir].each do |target|
+ existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_existing"))
+
+ target.symlink(existing_dir.path, { :force => true }).should == 0
+
+ existing_dir.symlink?.should be_false
+ end
+ end
+
+ it "should silently fail if trying to modify an existing directory symlink when using :force to reference a new file or directory" do
+ [file, dir].each do |target|
+ existing_dir = Puppet::FileSystem::File.new(tmpdir("#{target.path.basename.to_s}_existing"))
+ existing_symlink = Puppet::FileSystem::File.new(tmpfile("#{existing_dir.path.basename.to_s}_link"))
+ existing_dir.symlink(existing_symlink.path)
+
+ existing_symlink.readlink.should == existing_dir.path.to_s
+
+ target.symlink(existing_symlink.path, { :force => true }).should == 0
+
+ existing_symlink.readlink.should == existing_dir.path.to_s
+ end
+ end
+
+ it "should accept a string, Pathname or object with to_str (Puppet::Util::WatchedFile) for exist?" do
+ [ tmpfile('bogus1'),
+ Pathname.new(tmpfile('bogus2')),
+ Puppet::Util::WatchedFile.new(tmpfile('bogus3'))
+ ].each { |f| Puppet::FileSystem::File.exist?(f).should be_false }
+ end
+
+ it "should return a File::Stat instance when calling stat on an existing file" do
+ file.stat.should be_instance_of(File::Stat)
+ end
+
+ it "should raise Errno::ENOENT when calling stat on a missing file" do
+ expect { missing_file.stat }.to raise_error(Errno::ENOENT)
+ end
+
+ it "should be able to create a symlink, and verify it with symlink?" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ file.symlink(symlink.path)
+
+ symlink.symlink?.should be_true
+ end
+
+ it "should report symlink? as false on file, directory and missing files" do
+ [file, dir, missing_file].each do |f|
+ f.symlink?.should be_false
+ end
+ end
+
+ it "should return a File::Stat with ftype 'link' when calling lstat on a symlink pointing to existing file" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ file.symlink(symlink.path)
+
+ stat = symlink.lstat
+ stat.should be_instance_of(File::Stat)
+ stat.ftype.should == 'link'
+ end
+
+ it "should return a File::Stat of ftype 'link' when calling lstat on a symlink pointing to missing file" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ missing_file.symlink(symlink.path)
+
+ stat = symlink.lstat
+ stat.should be_instance_of(File::Stat)
+ stat.ftype.should == 'link'
+ end
+
+ it "should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to existing file" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ file.symlink(symlink.path)
+
+ stat = symlink.stat
+ stat.should be_instance_of(File::Stat)
+ stat.ftype.should == 'file'
+ end
+
+ it "should return a File::Stat of ftype 'directory' when calling stat on a symlink pointing to existing directory" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ dir.symlink(symlink.path)
+
+ stat = symlink.stat
+ stat.should be_instance_of(File::Stat)
+ stat.ftype.should == 'directory'
+ end
+
+ it "should return a File::Stat of ftype 'file' when calling stat on a symlink pointing to another symlink" do
+ # point symlink -> file
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ file.symlink(symlink.path)
+
+ # point symlink2 -> symlink
+ symlink2 = Puppet::FileSystem::File.new(tmpfile("somefile_link2"))
+ symlink.symlink(symlink2.path)
+
+ symlink2.stat.ftype.should == 'file'
+ end
+
+
+ it "should raise Errno::ENOENT when calling stat on a dangling symlink" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ missing_file.symlink(symlink.path)
+
+ expect { symlink.stat }.to raise_error(Errno::ENOENT)
+ end
+
+ it "should be able to readlink to resolve the physical path to a symlink" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ file.symlink(symlink.path)
+
+ file.exist?.should be_true
+ symlink.readlink.should == file.path.to_s
+ end
+
+ it "should not resolve entire symlink chain with readlink on a symlink'd symlink" do
+ # point symlink -> file
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ file.symlink(symlink.path)
+
+ # point symlink2 -> symlink
+ symlink2 = Puppet::FileSystem::File.new(tmpfile("somefile_link2"))
+ symlink.symlink(symlink2.path)
+
+ file.exist?.should be_true
+ symlink2.readlink.should == symlink.path.to_s
+ end
+
+ it "should be able to readlink to resolve the physical path to a dangling symlink" do
+ symlink = Puppet::FileSystem::File.new(tmpfile("somefile_link"))
+ missing_file.symlink(symlink.path)
+
+ missing_file.exist?.should be_false
+ symlink.readlink.should == missing_file.path.to_s
+ end
+
+ it "should delete only the symlink and not the target when calling unlink instance method" do
+ [file, dir].each do |target|
+ symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
+ target.symlink(symlink.path)
+
+ target.exist?.should be_true
+ symlink.readlink.should == target.path.to_s
+
+ symlink.unlink.should == 1 # count of files
+
+ target.exist?.should be_true
+ symlink.exist?.should be_false
+ end
+ end
+
+ it "should delete only the symlink and not the target when calling unlink class method" do
+ [file, dir].each do |target|
+ symlink = Puppet::FileSystem::File.new(tmpfile("#{target.path.basename.to_s}_link"))
+ target.symlink(symlink.path)
+
+ target.exist?.should be_true
+ symlink.readlink.should == target.path.to_s
+
+ Puppet::FileSystem::File.unlink(symlink.path).should == 1 # count of files
+
+ target.exist?.should be_true
+ symlink.exist?.should be_false
+ end
+ end
+
+ describe "unlink" do
+ it "should delete files with unlink" do
+ file.exist?.should be_true
+
+ file.unlink.should == 1 # count of files
+
+ file.exist?.should be_false
+ end
+
+ it "should delete files with unlink class method" do
+ file.exist?.should be_true
+
+ Puppet::FileSystem::File.unlink(file.path).should == 1 # count of files
+
+ file.exist?.should be_false
+ end
+
+ it "should delete multiple files with unlink class method" do
+ paths = (1..3).collect do |i|
+ f = Puppet::FileSystem::File.new(tmpfile("somefile_#{i}"))
+ FileUtils.touch(f.path)
+ f.exist?.should be_true
+ f.path.to_s
+ end
+
+ Puppet::FileSystem::File.unlink(*paths).should == 3 # count of files
+
+ paths.each { |p| Puppet::FileSystem::File.exist?(p).should be_false }
+ end
+
+ it "should raise Errno::EPERM or Errno::EISDIR when trying to delete a directory with the unlink class method" do
+ dir.exist?.should be_true
+
+ ex = nil
+ begin
+ Puppet::FileSystem::File.unlink(dir.path)
+ rescue Exception => e
+ ex = e
+ end
+
+ [
+ Errno::EPERM, # Windows and OSX
+ Errno::EISDIR # Linux
+ ].should include ex.class
+
+ dir.exist?.should be_true
+ end
+ end
+ end
+end
diff --git a/spec/unit/file_system/tempfile_spec.rb b/spec/unit/file_system/tempfile_spec.rb
new file mode 100644
index 000000000..5ad0a8abc
--- /dev/null
+++ b/spec/unit/file_system/tempfile_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Puppet::FileSystem::Tempfile do
+ it "makes the name of the file available" do
+ Puppet::FileSystem::Tempfile.open('foo') do |file|
+ expect(file.path).to match(/foo/)
+ end
+ end
+
+ it "provides a writeable file" do
+ Puppet::FileSystem::Tempfile.open('foo') do |file|
+ file.write("stuff")
+ file.flush
+
+ expect(Puppet::FileSystem::File.new(file.path).read).to eq("stuff")
+ end
+ end
+
+ it "returns the value of the block" do
+ the_value = Puppet::FileSystem::Tempfile.open('foo') do |file|
+ "my value"
+ end
+
+ expect(the_value).to eq("my value")
+ end
+
+ it "unlinks the temporary file" do
+ filename = Puppet::FileSystem::Tempfile.open('foo') do |file|
+ file.path
+ end
+
+ expect(Puppet::FileSystem::File.new(filename).exist?).to be_false
+ end
+
+ it "unlinks the temporary file even if the block raises an error" do
+ filename = nil
+
+ begin
+ Puppet::FileSystem::Tempfile.open('foo') do |file|
+ filename = file.path
+ raise "error!"
+ end
+ rescue
+ end
+
+ expect(Puppet::FileSystem::File.new(filename).exist?).to be_false
+ end
+end
diff --git a/spec/unit/graph/relationship_graph_spec.rb b/spec/unit/graph/relationship_graph_spec.rb
index 7a698d769..8bd35de4d 100755
--- a/spec/unit/graph/relationship_graph_spec.rb
+++ b/spec/unit/graph/relationship_graph_spec.rb
@@ -165,12 +165,6 @@ describe Puppet::Graph::RelationshipGraph do
expect(order_resources_traversed_in(relationships)).to(
include_in_order("Notify[a]", "Notify[b]", "Notify[c]", "Notify[d]"))
end
-
- def order_resources_traversed_in(relationships)
- order_seen = []
- relationships.traverse { |resource| order_seen << resource.ref }
- order_seen
- end
end
describe "when interrupting traversal" do
diff --git a/spec/unit/hiera_puppet_spec.rb b/spec/unit/hiera_puppet_spec.rb
index 6c0f882a0..894b6e334 100644
--- a/spec/unit/hiera_puppet_spec.rb
+++ b/spec/unit/hiera_puppet_spec.rb
@@ -52,7 +52,7 @@ describe 'HieraPuppet' do
pending("This example does not apply to Puppet #{Puppet.version} because it does not have this setting")
end
- File.stubs(:exist?).with(Puppet[:hiera_config]).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:hiera_config]).returns(true)
HieraPuppet.send(:hiera_config_file).should == Puppet[:hiera_config]
end
@@ -64,7 +64,7 @@ describe 'HieraPuppet' do
end
Puppet.settings[:confdir] = "/dev/null/puppet"
hiera_config = File.join(Puppet[:confdir], 'hiera.yaml')
- File.stubs(:exist?).with(hiera_config).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(hiera_config).returns(true)
HieraPuppet.send(:hiera_config_file).should == hiera_config
end
diff --git a/spec/unit/indirector/catalog/compiler_spec.rb b/spec/unit/indirector/catalog/compiler_spec.rb
index ff32f7c7b..c968794e4 100755
--- a/spec/unit/indirector/catalog/compiler_spec.rb
+++ b/spec/unit/indirector/catalog/compiler_spec.rb
@@ -24,12 +24,12 @@ describe Puppet::Resource::Catalog::Compiler do
end
it "should cache the server metadata and reuse it" do
+ Puppet[:node_terminus] = :memory
+ Puppet::Node.indirection.save(Puppet::Node.new("node1"))
+ Puppet::Node.indirection.save(Puppet::Node.new("node2"))
+
compiler = Puppet::Resource::Catalog::Compiler.new
- node1 = stub 'node1', :merge => nil
- node2 = stub 'node2', :merge => nil
compiler.stubs(:compile)
- Puppet::Node.indirection.stubs(:find).with('node1', has_entry(:environment => anything)).returns(node1)
- Puppet::Node.indirection.stubs(:find).with('node2', has_entry(:environment => anything)).returns(node2)
compiler.find(Puppet::Indirector::Request.new(:catalog, :find, 'node1', nil, :node => 'node1'))
compiler.find(Puppet::Indirector::Request.new(:catalog, :find, 'node2', nil, :node => 'node2'))
@@ -175,19 +175,16 @@ describe Puppet::Resource::Catalog::Compiler do
end
describe "when finding nodes" do
- before do
+ it "should look node information up via the Node class with the provided key" do
Facter.stubs(:value).returns("whatever")
- @compiler = Puppet::Resource::Catalog::Compiler.new
- @name = "me"
- @node = mock 'node'
- @request = Puppet::Indirector::Request.new(:catalog, :find, @name, nil)
- @compiler.stubs(:compile)
- end
+ node = Puppet::Node.new('node')
+ compiler = Puppet::Resource::Catalog::Compiler.new
+ request = Puppet::Indirector::Request.new(:catalog, :find, "me", nil)
+ compiler.stubs(:compile)
- it "should look node information up via the Node class with the provided key" do
- @node.stubs :merge
- Puppet::Node.indirection.expects(:find).with(@name, anything).returns(@node)
- @compiler.find(@request)
+ Puppet::Node.indirection.expects(:find).with("me", anything).returns(node)
+
+ compiler.find(request)
end
end
@@ -197,11 +194,10 @@ describe Puppet::Resource::Catalog::Compiler do
Facter.expects(:value).with('fqdn').returns("my.server.com")
Facter.expects(:value).with('ipaddress').returns("my.ip.address")
@compiler = Puppet::Resource::Catalog::Compiler.new
- @name = "me"
- @node = mock 'node'
- @request = Puppet::Indirector::Request.new(:catalog, :find, @name, nil)
+ @node = Puppet::Node.new("me")
+ @request = Puppet::Indirector::Request.new(:catalog, :find, "me", nil)
@compiler.stubs(:compile)
- Puppet::Node.indirection.stubs(:find).with(@name, anything).returns(@node)
+ Puppet::Node.indirection.stubs(:find).with("me", anything).returns(@node)
end
it "should add the server's Puppet version to the node's parameters as 'serverversion'" do
diff --git a/spec/unit/indirector/certificate_status/file_spec.rb b/spec/unit/indirector/certificate_status/file_spec.rb
index c880bf786..83d4de210 100755
--- a/spec/unit/indirector/certificate_status/file_spec.rb
+++ b/spec/unit/indirector/certificate_status/file_spec.rb
@@ -87,16 +87,14 @@ describe "Puppet::Indirector::CertificateStatus::File" do
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
- pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do
- generate_signed_cert(@host)
- request = Puppet::Indirector::Request.new(:certificate_status, :find, "foo", @host)
+ it "should return the Puppet::SSL::Host when a public key exists 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 = @terminus.find(request)
- retrieved_host.name.should == @host.name
- retrieved_host.certificate.content.to_s.chomp.should == @host.certificate.content.to_s.chomp
- end
+ 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
@@ -122,12 +120,10 @@ describe "Puppet::Indirector::CertificateStatus::File" do
end
it "should sign the on-disk CSR when it is present" do
- pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do
- signed_host = generate_signed_cert(@host)
+ 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
+ signed_host.state.should == "signed"
+ Puppet::SSL::Certificate.indirection.find("foobar").should be_instance_of(Puppet::SSL::Certificate)
end
end
@@ -142,11 +138,9 @@ describe "Puppet::Indirector::CertificateStatus::File" do
end
it "should revoke the certificate when it is present" do
- pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do
- generate_revoked_cert(@host)
+ generate_revoked_cert(@host)
- @host.state.should == 'revoked'
- end
+ @host.state.should == 'revoked'
end
end
end
@@ -163,39 +157,35 @@ describe "Puppet::Indirector::CertificateStatus::File" do
end
it "should clean certs, cert requests, keys" do
- pending("porting to Windows", :if => Puppet.features.microsoft_windows?) 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
+ 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
- pending("porting to Windows", :if => Puppet.features.microsoft_windows?) do
- Puppet.settings.use(:main)
+ Puppet.settings.use(:main)
- signed_host = Puppet::SSL::Host.new("signed_host")
- generate_signed_cert(signed_host)
+ 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)
+ 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)
+ 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))
+ 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
+ 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/data_binding/hiera_spec.rb b/spec/unit/indirector/data_binding/hiera_spec.rb
index 98883bb9d..7ab3b27fc 100644
--- a/spec/unit/indirector/data_binding/hiera_spec.rb
+++ b/spec/unit/indirector/data_binding/hiera_spec.rb
@@ -2,8 +2,32 @@ require 'spec_helper'
require 'puppet/indirector/data_binding/hiera'
describe Puppet::DataBinding::Hiera do
- it "should be a subclass of the Hiera terminus" do
- Puppet::DataBinding::Hiera.superclass.should equal(Puppet::Indirector::Hiera)
+ include PuppetSpec::Files
+
+ def write_hiera_config(config_file, datadir)
+ File.open(config_file, 'w') do |f|
+ f.write("---
+ :yaml:
+ :datadir: #{datadir}
+ :hierarchy: ['global', 'invalid']
+ :logger: 'noop'
+ :backends: ['yaml']
+ ")
+ end
+ end
+
+ def request(key)
+ Puppet::Indirector::Request.new(:hiera, :find, key, nil)
+ end
+
+ before do
+ hiera_config_file = tmpfile("hiera.yaml")
+ Puppet.settings[:hiera_config] = hiera_config_file
+ write_hiera_config(hiera_config_file, my_fixture_dir)
+ end
+
+ after do
+ Puppet::DataBinding::Hiera.instance_variable_set(:@hiera, nil)
end
it "should have documentation" do
@@ -18,4 +42,73 @@ describe Puppet::DataBinding::Hiera do
it "should have its name set to :hiera" do
Puppet::DataBinding::Hiera.name.should == :hiera
end
+ it "should be the default data_binding terminus" do
+ Puppet.settings[:data_binding_terminus].should == :hiera
+ end
+
+ it "should raise an error if we don't have the hiera feature" do
+ Puppet.features.expects(:hiera?).returns(false)
+ lambda { Puppet::DataBinding::Hiera.new }.should raise_error RuntimeError,
+ "Hiera terminus not supported without hiera library"
+ end
+
+ describe "the behavior of the hiera_config method", :if => Puppet.features.hiera? do
+ it "should override the logger and set it to puppet" do
+ Puppet::DataBinding::Hiera.hiera_config[:logger].should == "puppet"
+ end
+
+ context "when the Hiera configuration file does not exist" do
+ let(:path) { File.expand_path('/doesnotexist') }
+
+ before do
+ Puppet.settings[:hiera_config] = path
+ end
+
+ it "should log a warning" do
+ Puppet.expects(:warning).with(
+ "Config file #{path} not found, using Hiera defaults")
+ Puppet::DataBinding::Hiera.hiera_config
+ end
+
+ it "should only configure the logger and set it to puppet" do
+ Puppet.expects(:warning).with(
+ "Config file #{path} not found, using Hiera defaults")
+ Puppet::DataBinding::Hiera.hiera_config.should == { :logger => 'puppet' }
+ end
+ end
+ end
+
+ describe "the behavior of the find method", :if => Puppet.features.hiera? do
+
+ let(:data_binder) { Puppet::DataBinding::Hiera.new }
+
+ it "should support looking up an integer" do
+ data_binder.find(request("integer")).should == 3000
+ end
+
+ it "should support looking up a string" do
+ data_binder.find(request("string")).should == 'apache'
+ end
+
+ it "should support looking up an array" do
+ data_binder.find(request("array")).should == [
+ '0.ntp.puppetlabs.com',
+ '1.ntp.puppetlabs.com',
+ ]
+ end
+
+ it "should support looking up a hash" do
+ data_binder.find(request("hash")).should == {
+ 'user' => 'Hightower',
+ 'group' => 'admin',
+ 'mode' => '0644'
+ }
+ end
+
+ it "raises a data binding error if hiera cannot parse the yaml data" do
+ expect do
+ data_binder.find(request('invalid'))
+ end.to raise_error(Puppet::DataBinding::LookupError)
+ end
+ end
end
diff --git a/spec/unit/indirector/direct_file_server_spec.rb b/spec/unit/indirector/direct_file_server_spec.rb
index 234f25ef9..8b08195bb 100755
--- a/spec/unit/indirector/direct_file_server_spec.rb
+++ b/spec/unit/indirector/direct_file_server_spec.rb
@@ -26,12 +26,12 @@ describe Puppet::Indirector::DirectFileServer do
describe Puppet::Indirector::DirectFileServer, "when finding a single file" do
it "should return nil if the file does not exist" do
- FileTest.expects(:exists?).with(@path).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns false
@server.find(@request).should be_nil
end
it "should return a Content instance created with the full path to the file if the file exists" do
- FileTest.expects(:exists?).with(@path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns true
@model.expects(:new).returns(:mycontent)
@server.find(@request).should == :mycontent
end
@@ -42,7 +42,7 @@ describe Puppet::Indirector::DirectFileServer do
before do
@data = mock 'content'
@data.stubs(:collect)
- FileTest.expects(:exists?).with(@path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns true
end
it "should pass the full path to the instance" do
@@ -61,18 +61,18 @@ describe Puppet::Indirector::DirectFileServer do
describe Puppet::Indirector::DirectFileServer, "when searching for multiple files" do
it "should return nil if the file does not exist" do
- FileTest.expects(:exists?).with(@path).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns false
@server.find(@request).should be_nil
end
it "should use :path2instances from the terminus_helper to return instances if the file exists" do
- FileTest.expects(:exists?).with(@path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns true
@server.expects(:path2instances)
@server.search(@request)
end
it "should pass the original request to :path2instances" do
- FileTest.expects(:exists?).with(@path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns true
@server.expects(:path2instances).with(@request, @path)
@server.search(@request)
end
diff --git a/spec/unit/indirector/facts/facter_spec.rb b/spec/unit/indirector/facts/facter_spec.rb
index 3e301a5a5..4c55ec6c3 100755
--- a/spec/unit/indirector/facts/facter_spec.rb
+++ b/spec/unit/indirector/facts/facter_spec.rb
@@ -48,6 +48,9 @@ describe Puppet::Node::Facts::Facter do
Facter.stubs(:to_hash).returns({})
@name = "me"
@request = stub 'request', :key => @name
+ @environment = stub 'environment'
+ @request.stubs(:environment).returns(@environment)
+ @request.environment.stubs(:modules).returns([])
end
describe Puppet::Node::Facts::Facter, " when finding facts" do
@@ -58,6 +61,15 @@ describe Puppet::Node::Facts::Facter do
@facter.find(@request)
end
+ it "should include external facts when feature is present" do
+ clear = sequence 'clear'
+ Puppet.features.stubs(:external_facts?).returns(:true)
+ Puppet::Node::Facts::Facter.expects(:setup_external_facts).in_sequence(clear)
+ Puppet::Node::Facts::Facter.expects(:reload_facter).in_sequence(clear)
+ Puppet::Node::Facts::Facter.expects(:load_fact_plugins).in_sequence(clear)
+ @facter.find(@request)
+ end
+
it "should return a Facts instance" do
@facter.find(@request).should be_instance_of(Puppet::Node::Facts)
end
@@ -131,6 +143,20 @@ describe Puppet::Node::Facts::Facter do
Puppet::Node::Facts::Facter.load_facts_in_dir("mydir")
end
+ it "should include pluginfactdest when loading external facts",
+ :if => Puppet.features.external_facts?, :unless => Puppet.features.microsoft_windows? do
+ Puppet[:pluginfactdest] = "/plugin/dest"
+ @facter.find(@request)
+ Facter::Util::Config.external_facts_dirs.include?("/plugin/dest")
+ end
+
+ it "should include pluginfactdest when loading external facts",
+ :if => Puppet.features.external_facts?, :if => Puppet.features.microsoft_windows? do
+ Puppet[:pluginfactdest] = "/plugin/dest"
+ @facter.find(@request)
+ Facter::Util::Config.external_facts_dirs.include?("C:/plugin/dest")
+ end
+
describe "when loading fact plugins from disk" do
let(:one) { File.expand_path("one") }
let(:two) { File.expand_path("two") }
@@ -160,5 +186,12 @@ describe Puppet::Node::Facts::Facter do
Puppet::Node::Facts::Facter.load_fact_plugins
end
+
+ it "should include module plugin facts when present", :if => Puppet.features.external_facts? do
+ mod = Puppet::Module.new("mymodule", "#{one}/mymodule", @request.environment)
+ @request.environment.stubs(:modules).returns([mod])
+ @facter.find(@request)
+ Facter::Util::Config.external_facts_dirs.include?("#{one}/mymodule/facts.d")
+ 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 37529e5d5..2f9e140f7 100755
--- a/spec/unit/indirector/file_bucket_file/file_spec.rb
+++ b/spec/unit/indirector/file_bucket_file/file_spec.rb
@@ -2,18 +2,11 @@
require 'spec_helper'
require 'puppet/indirector/file_bucket_file/file'
+require 'puppet/util/platform'
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
include PuppetSpec::Files
@@ -23,7 +16,7 @@ describe Puppet::FileBucketFile::File do
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}")
+ Puppet::FileBucket::File.indirection.save(bucket_file, "#{bucket_file.name}#{path}")
bucket_file.checksum_data
end
@@ -34,17 +27,58 @@ describe Puppet::FileBucketFile::File do
result.contents.should be_empty
end
+ it "deals with multiple processes saving at the same time", :unless => Puppet::Util::Platform.windows? do
+ bucket_file = Puppet::FileBucket::File.new("contents")
+
+ children = []
+ 5.times do |count|
+ children << Kernel.fork do
+ save_bucket_file("contents", "/testing")
+ exit(0)
+ end
+ end
+ children.each { |child| Process.wait(child) }
+
+ paths = File.read("#{Puppet[:bucketdir]}/9/8/b/f/7/d/8/c/98bf7d8c15784f0a3d63204441e1e2aa/paths").lines.to_a
+ paths.length.should == 1
+ Puppet::FileBucket::File.indirection.head("#{bucket_file.checksum_type}/#{bucket_file.checksum_data}/testing").should be_true
+ end
+
+ it "fails if the contents collide with existing contents" do
+ # This is the shortest known MD5 collision. See http://eprint.iacr.org/2010/643.pdf
+ first_contents = [0x6165300e,0x87a79a55,0xf7c60bd0,0x34febd0b,
+ 0x6503cf04,0x854f709e,0xfb0fc034,0x874c9c65,
+ 0x2f94cc40,0x15a12deb,0x5c15f4a3,0x490786bb,
+ 0x6d658673,0xa4341f7d,0x8fd75920,0xefd18d5a].pack("I" * 16)
+
+ collision_contents = [0x6165300e,0x87a79a55,0xf7c60bd0,0x34febd0b,
+ 0x6503cf04,0x854f749e,0xfb0fc034,0x874c9c65,
+ 0x2f94cc40,0x15a12deb,0xdc15f4a3,0x490786bb,
+ 0x6d658673,0xa4341f7d,0x8fd75920,0xefd18d5a].pack("I" * 16)
+
+ save_bucket_file(first_contents, "/foo/bar")
+
+ expect do
+ save_bucket_file(collision_contents, "/foo/bar")
+ end.to raise_error(Puppet::FileBucket::BucketError, /Got passed new contents/)
+ end
+
describe "when supplying a path" do
it "should store the path if not already stored" do
checksum = save_bucket_file("stuff\r\n", "/foo/bar")
+
dir_path = "#{Puppet[:bucketdir]}/f/c/7/7/7/c/0/b/fc777c0bc467e1ab98b4c6915af802ec"
- IO.binread("#{dir_path}/contents").should == "stuff\r\n"
- File.read("#{dir_path}/paths").should == "foo/bar\n"
+ contents_file = Puppet::FileSystem::File.new("#{dir_path}/contents")
+ paths_file = Puppet::FileSystem::File.new("#{dir_path}/paths")
+ contents_file.binread.should == "stuff\r\n"
+ paths_file.read.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"
@@ -52,7 +86,9 @@ describe Puppet::FileBucketFile::File do
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"
@@ -62,6 +98,7 @@ describe Puppet::FileBucketFile::File do
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 == ""
@@ -78,16 +115,18 @@ describe Puppet::FileBucketFile::File do
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
@@ -105,10 +144,11 @@ describe Puppet::FileBucketFile::File do
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
@@ -126,6 +166,7 @@ describe Puppet::FileBucketFile::File do
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 == <<HERE
2c2
@@ -136,27 +177,23 @@ 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}"
+ checksum = save_bucket_file("whatever")
+
+ expect do
+ Puppet::FileBucket::File.indirection.find("md5/#{checksum}", :diff_with => bogus_checksum)
+ end.to 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"
+ checksum = save_bucket_file("whatever")
+
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|
@@ -245,32 +282,4 @@ HERE
end
end
end
-
- describe "when verifying identical files" do
- let(:contents) { "file\r\n contents" }
- let(:digest) { "8b3702ad1aed1ace7e32bde76ffffb2d" }
- let(:checksum) { "{md5}#{@digest}" }
- let(:bucketdir) { tmpdir('file_bucket_file') }
- let(:destdir) { "#{bucketdir}/8/b/3/7/0/2/a/d/8b3702ad1aed1ace7e32bde76ffffb2d" }
- let(:bucket) { Puppet::FileBucket::File.new(contents) }
-
- before :each do
- Puppet[:bucketdir] = bucketdir
- FileUtils.mkdir_p(destdir)
- end
-
- it "should raise an error if the files don't match" do
- File.open(File.join(destdir, 'contents'), 'wb') { |f| f.print "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.open(File.join(destdir, 'contents'), 'wb') { |f| f.print contents }
-
- Puppet::FileBucketFile::File.new.send(:verify_identical_file!, bucket)
- end
- end
end
diff --git a/spec/unit/indirector/file_metadata/file_spec.rb b/spec/unit/indirector/file_metadata/file_spec.rb
index d51c65882..459c1dbe8 100755
--- a/spec/unit/indirector/file_metadata/file_spec.rb
+++ b/spec/unit/indirector/file_metadata/file_spec.rb
@@ -19,7 +19,7 @@ describe Puppet::Indirector::FileMetadata::File do
@uri = Puppet::Util.path_to_uri(@path).to_s
@data = mock 'metadata'
@data.stubs(:collect)
- FileTest.expects(:exists?).with(@path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns true
@request = Puppet::Indirector::Request.new(:file_metadata, :find, @uri, nil)
end
@@ -42,7 +42,7 @@ describe Puppet::Indirector::FileMetadata::File do
end
it "should collect the attributes of the instances returned" do
- FileTest.expects(:exists?).with(@path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns true
@metadata.expects(:path2instances).returns( [mock("one", :collect => nil), mock("two", :collect => nil)] )
@metadata.search(@request)
end
diff --git a/spec/unit/indirector/file_server_spec.rb b/spec/unit/indirector/file_server_spec.rb
index 00649875f..6f491b065 100755
--- a/spec/unit/indirector/file_server_spec.rb
+++ b/spec/unit/indirector/file_server_spec.rb
@@ -154,7 +154,7 @@ describe Puppet::Indirector::FileServer do
@mount.expects(:search).with { |key, request| key == "rel/path" }.returns %w{/one /two}
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
one = mock 'fileset_one'
Puppet::FileServing::Fileset.expects(:new).with("/one", @request).returns(one)
@@ -171,7 +171,7 @@ describe Puppet::Indirector::FileServer do
@mount.expects(:search).with { |key, request| key == "rel/path" }.returns []
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one", "two" => "/two")
@@ -193,7 +193,7 @@ describe Puppet::Indirector::FileServer do
@mount.expects(:search).with { |key, request| key == "rel/path" }.returns []
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one")
@@ -211,7 +211,7 @@ describe Puppet::Indirector::FileServer do
@mount.expects(:search).with { |key, options| key == "rel/path" }.returns []
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Puppet::FileServing::Fileset.expects(:merge).returns("one" => "/one")
diff --git a/spec/unit/indirector/hiera_spec.rb b/spec/unit/indirector/hiera_spec.rb
deleted file mode 100644
index 36b598c37..000000000
--- a/spec/unit/indirector/hiera_spec.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-require 'spec_helper'
-require 'puppet/indirector/hiera'
-require 'hiera/backend'
-
-describe Puppet::Indirector::Hiera do
- include PuppetSpec::Files
-
- def write_hiera_config(config_file, datadir)
- File.open(config_file, 'w') do |f|
- f.write("---
- :yaml:
- :datadir: #{datadir}
- :hierarchy: ['global']
- :logger: 'noop'
- :backends: ['yaml']
- ")
- end
- end
-
- before do
- Puppet.settings[:hiera_config] = hiera_config_file
- write_hiera_config(hiera_config_file, datadir)
-
- Puppet::Indirector::Terminus.stubs(:register_terminus_class)
- Puppet::Indirector::Indirection.stubs(:instance).returns(indirection)
-
- module Testing; end
- @hiera_class = class Testing::Hiera < Puppet::Indirector::Hiera
- self
- end
- end
-
- let(:model) { mock('model') }
- let(:options) { {:host => 'foo' } }
-
- let(:request_integer) do
- stub('request', :key => "integer", :options => options)
- end
-
- let(:request_string) do
- stub('request', :key => "string", :options => options)
- end
-
- let(:request_array) do
- stub('request', :key => "array", :options => options)
- end
-
- let(:request_hash) do
- stub('request', :key => "hash", :options => options)
- end
-
- let(:indirection) do
- stub('indirection', :name => :none, :register_terminus_type => nil,
- :model => model)
- end
-
- let(:facts) do
- { 'fqdn' => 'agent.testing.com' }
- end
- let(:facter_obj) { stub(:values => facts) }
-
- let(:hiera_config_file) do
- tmpfile("hiera.yaml")
- end
-
- let(:datadir) { my_fixture_dir }
-
- it "should be the default data_binding terminus" do
- Puppet.settings[:data_binding_terminus].should == :hiera
- end
-
- it "should raise an error if we don't have the hiera feature" do
- Puppet.features.expects(:hiera?).returns(false)
- lambda { @hiera_class.new }.should raise_error RuntimeError,
- "Hiera terminus not supported without hiera library"
- end
-
- describe "the behavior of the hiera_config method", :if => Puppet.features.hiera? do
- let(:default_hiera_config) do
- {
- :logger => "puppet",
- :backends => ["yaml"],
- :yaml => { :datadir => datadir },
- :hierarchy => ["global"]
- }
- end
-
- it "should load the hiera config file by delegating to Hiera" do
- Hiera::Config.expects(:load).with(hiera_config_file).returns({})
- @hiera_class.hiera_config
- end
-
- it "should override the logger and set it to puppet" do
- @hiera_class.hiera_config[:logger].should == "puppet"
- end
-
- it "should return a hiera configuration hash" do
- results = @hiera_class.hiera_config
- default_hiera_config.each do |key,value|
- results[key].should == value
- end
- results.should be_a_kind_of Hash
- end
-
- context "when the Hiera configuration file does not exist" do
- let(:path) { File.expand_path('/doesnotexist') }
-
- before do
- Puppet.settings[:hiera_config] = path
- end
-
- it "should log a warning" do
- Puppet.expects(:warning).with(
- "Config file #{path} not found, using Hiera defaults")
- @hiera_class.hiera_config
- end
-
- it "should only configure the logger and set it to puppet" do
- Puppet.expects(:warning).with(
- "Config file #{path} not found, using Hiera defaults")
- @hiera_class.hiera_config.should == { :logger => 'puppet' }
- end
- end
- end
-
- describe "the behavior of the find method", :if => Puppet.features.hiera? do
-
- let(:data_binder) { @hiera_class.new }
-
- it "should support looking up an integer" do
- data_binder.find(request_integer).should == 3000
- end
-
- it "should support looking up a string" do
- data_binder.find(request_string).should == 'apache'
- end
-
- it "should support looking up an array" do
- data_binder.find(request_array).should == [
- '0.ntp.puppetlabs.com',
- '1.ntp.puppetlabs.com',
- ]
- end
-
- it "should support looking up a hash" do
- data_binder.find(request_hash).should == {
- 'user' => 'Hightower',
- 'group' => 'admin',
- 'mode' => '0644'
- }
- end
- end
-end
-
diff --git a/spec/unit/indirector/json_spec.rb b/spec/unit/indirector/json_spec.rb
index c80bcd33c..664a32749 100755
--- a/spec/unit/indirector/json_spec.rb
+++ b/spec/unit/indirector/json_spec.rb
@@ -131,20 +131,20 @@ describe Puppet::Indirector::JSON do
with_content('hello') do
subject.destroy(request)
end
- File.should_not be_exist file
+ Puppet::FileSystem::File.exist?(file).should be_false
end
it "silently succeeds when files don't exist" do
- File.unlink(file) rescue nil
+ Puppet::FileSystem::File.unlink(file) rescue nil
subject.destroy(request).should be_true
end
it "raises an informative error for other failures" do
- File.stubs(:unlink).with(file).raises(Errno::EPERM, 'fake permission problem')
+ Puppet::FileSystem::File.stubs(:unlink).with(file).raises(Errno::EPERM, 'fake permission problem')
with_content('hello') do
expect { subject.destroy(request) }.to raise_error Puppet::Error
end
- File.unstub(:unlink) # thanks, mocha
+ Puppet::FileSystem::File.unstub(:unlink) # thanks, mocha
end
end
end
diff --git a/spec/unit/indirector/key/file_spec.rb b/spec/unit/indirector/key/file_spec.rb
index 57be73691..95c212523 100755
--- a/spec/unit/indirector/key/file_spec.rb
+++ b/spec/unit/indirector/key/file_spec.rb
@@ -64,33 +64,32 @@ describe Puppet::SSL::Key::File do
end
it "should save the public key when saving the private key" do
- Puppet.settings.stubs(:writesub)
+ fh = StringIO.new
- fh = mock 'filehandle'
-
- Puppet.settings.expects(:writesub).with(:publickeydir, @public_key_path).yields fh
+ Puppet.settings.setting(:publickeydir).expects(:open_file).with(@public_key_path, 'w').yields fh
+ Puppet.settings.setting(:privatekeydir).stubs(:open_file)
@public_key.expects(:to_pem).returns "my pem"
- fh.expects(:print).with "my pem"
-
@searcher.save(@request)
+
+ expect(fh.string).to eq("my pem")
end
it "should destroy the public key when destroying the private key" do
- File.stubs(:unlink).with(@private_key_path)
- FileTest.stubs(:exist?).with(@private_key_path).returns true
- FileTest.expects(:exist?).with(@public_key_path).returns true
- File.expects(:unlink).with(@public_key_path)
+ Puppet::FileSystem::File.stubs(:unlink).with(@private_key_path)
+ Puppet::FileSystem::File.stubs(:exist?).with(@private_key_path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@public_key_path).returns true
+ Puppet::FileSystem::File.expects(:unlink).with(@public_key_path)
@searcher.destroy(@request)
end
it "should not fail if the public key does not exist when deleting the private key" do
- File.stubs(:unlink).with(@private_key_path)
+ Puppet::FileSystem::File.stubs(:unlink).with(@private_key_path)
- FileTest.stubs(:exist?).with(@private_key_path).returns true
- FileTest.expects(:exist?).with(@public_key_path).returns false
- File.expects(:unlink).with(@public_key_path).never
+ Puppet::FileSystem::File.stubs(:exist?).with(@private_key_path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@public_key_path).returns false
+ Puppet::FileSystem::File.expects(:unlink).with(@public_key_path).never
@searcher.destroy(@request)
end
diff --git a/spec/unit/indirector/resource/ral_spec.rb b/spec/unit/indirector/resource/ral_spec.rb
index 0e14a322b..8b42f6881 100755
--- a/spec/unit/indirector/resource/ral_spec.rb
+++ b/spec/unit/indirector/resource/ral_spec.rb
@@ -2,6 +2,13 @@
require 'spec_helper'
describe "Puppet::Resource::Ral" do
+
+ it "is deprecated on the network, but still allows requests" do
+ Puppet.expects(:deprecation_warning)
+
+ expect(Puppet::Resource::Ral.new.allow_remote_requests?).to eq(true)
+ end
+
describe "find" do
before do
@request = stub 'request', :key => "user/root"
diff --git a/spec/unit/indirector/resource/store_configs_spec.rb b/spec/unit/indirector/resource/store_configs_spec.rb
index aab2a4475..0ddb94470 100755
--- a/spec/unit/indirector/resource/store_configs_spec.rb
+++ b/spec/unit/indirector/resource/store_configs_spec.rb
@@ -9,4 +9,15 @@ end
describe Puppet::Resource::StoreConfigs do
it_should_behave_like "a StoreConfigs terminus"
+
+ before :each do
+ Puppet[:storeconfigs] = true
+ Puppet[:storeconfigs_backend] = "store_configs_testing"
+ end
+
+ it "is deprecated on the network, but still allows requests" do
+ Puppet.expects(:deprecation_warning)
+
+ expect(Puppet::Resource::StoreConfigs.new.allow_remote_requests?).to eq(true)
+ end
end
diff --git a/spec/unit/indirector/rest_spec.rb b/spec/unit/indirector/rest_spec.rb
index 787c4cb5e..0bf0bcb48 100755
--- a/spec/unit/indirector/rest_spec.rb
+++ b/spec/unit/indirector/rest_spec.rb
@@ -109,6 +109,10 @@ describe Puppet::Indirector::REST do
string.split(',').collect { |s| convert_from(format, s) }
end
+ def to_data_hash
+ { 'name' => @name, 'data' => @data }
+ end
+
def ==(other)
other.is_a? Puppet::TestModel and other.name == name and other.data == data
end
@@ -205,21 +209,21 @@ describe Puppet::Indirector::REST do
@request = stub 'request', :key => "foo", :server => nil, :port => nil
terminus.class.expects(:port).returns 321
terminus.class.expects(:server).returns "myserver"
- Puppet::Network::HTTP::Connection.expects(:new).with("myserver", 321).returns "myconn"
+ Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn"
terminus.network(@request).should == "myconn"
end
it "should use the server from the indirection request if one is present" do
@request = stub 'request', :key => "foo", :server => "myserver", :port => nil
terminus.class.stubs(:port).returns 321
- Puppet::Network::HTTP::Connection.expects(:new).with("myserver", 321).returns "myconn"
+ Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn"
terminus.network(@request).should == "myconn"
end
it "should use the port from the indirection request if one is present" do
@request = stub 'request', :key => "foo", :server => nil, :port => 321
terminus.class.stubs(:server).returns "myserver"
- Puppet::Network::HTTP::Connection.expects(:new).with("myserver", 321).returns "myconn"
+ Puppet::Network::HttpPool.expects(:http_instance).with("myserver", 321).returns "myconn"
terminus.network(@request).should == "myconn"
end
end
diff --git a/spec/unit/indirector/ssl_file_spec.rb b/spec/unit/indirector/ssl_file_spec.rb
index 4cc0e216f..8406cc9d7 100755
--- a/spec/unit/indirector/ssl_file_spec.rb
+++ b/spec/unit/indirector/ssl_file_spec.rb
@@ -121,9 +121,9 @@ describe Puppet::Indirector::SslFile do
describe "when finding certificates on disk" do
describe "and no certificate is present" do
it "should return nil" do
- FileTest.expects(:exist?).with(@path).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns(true)
Dir.expects(:entries).with(@path).returns([])
- FileTest.expects(:exist?).with(@certpath).returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns(false)
@searcher.find(@request).should be_nil
end
@@ -139,7 +139,7 @@ describe Puppet::Indirector::SslFile do
context "is readable" do
it "should return an instance of the model, which it should use to read the certificate" do
- FileTest.expects(:exist?).with(@certpath).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns true
model.expects(:new).with("myname").returns cert
cert.expects(:read).with(@certpath)
@@ -150,7 +150,7 @@ describe Puppet::Indirector::SslFile do
context "is unreadable" do
it "should raise an exception" do
- FileTest.expects(:exist?).with(@certpath).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns(true)
model.expects(:new).with("myname").returns cert
cert.expects(:read).with(@certpath).raises(Errno::EACCES)
@@ -171,9 +171,9 @@ describe Puppet::Indirector::SslFile do
# 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)
+ Puppet::FileSystem::File.expects(:exist?).with(@path).returns(false)
dir, file = File.split(@path)
- FileTest.expects(:exist?).with(dir).returns true
+ Puppet::FileSystem::File.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)
@@ -221,7 +221,7 @@ describe Puppet::Indirector::SslFile do
@searcher.class.store_in @setting
fh = mock 'filehandle'
fh.stubs :print
- Puppet.settings.expects(:writesub).with(@setting, @certpath).yields fh
+ Puppet.settings.setting(@setting).expects(:open_file).with(@certpath, 'w').yields fh
@searcher.save(@request)
end
@@ -233,7 +233,7 @@ describe Puppet::Indirector::SslFile do
fh = mock 'filehandle'
fh.stubs :print
- Puppet.settings.expects(:write).with(@setting).yields fh
+ Puppet.settings.setting(@setting).expects(:open).with('w').yields fh
@searcher.save(@request)
end
end
@@ -246,7 +246,7 @@ describe Puppet::Indirector::SslFile do
fh = mock 'filehandle'
fh.stubs :print
- Puppet.settings.expects(:write).with(:cakey).yields fh
+ Puppet.settings.setting(:cakey).expects(:open).with('w').yields fh
@searcher.stubs(:ca?).returns true
@searcher.save(@request)
end
@@ -256,7 +256,7 @@ describe Puppet::Indirector::SslFile do
describe "when destroying certificates" do
describe "that do not exist" do
before do
- FileTest.expects(:exist?).with(@certpath).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns false
end
it "should return false" do
@@ -265,18 +265,15 @@ describe Puppet::Indirector::SslFile do
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)
+ Puppet::FileSystem::File.expects(:exist?).with(@certpath).returns true
+ Puppet::FileSystem::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::FileSystem::File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:unlink)
Puppet.expects(:notice)
@searcher.destroy(@request)
end
diff --git a/spec/unit/indirector/yaml_spec.rb b/spec/unit/indirector/yaml_spec.rb
index 8a2753479..6830869b1 100755
--- a/spec/unit/indirector/yaml_spec.rb
+++ b/spec/unit/indirector/yaml_spec.rb
@@ -149,15 +149,15 @@ describe Puppet::Indirector::Yaml do
end
it "should unlink the right yaml file if it exists" do
- File.expects(:exists?).with(path).returns true
- File.expects(:unlink).with(path)
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns true
+ Puppet::FileSystem::File.expects(:unlink).with(path)
@store.destroy(@request)
end
it "should not unlink the yaml file if it does not exists" do
- File.expects(:exists?).with(path).returns false
- File.expects(:unlink).with(path).never
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns false
+ Puppet::FileSystem::File.expects(:unlink).with(path).never
@store.destroy(@request)
end
diff --git a/spec/unit/module_spec.rb b/spec/unit/module_spec.rb
index c06f278eb..5c4065e14 100755
--- a/spec/unit/module_spec.rb
+++ b/spec/unit/module_spec.rb
@@ -15,7 +15,7 @@ describe Puppet::Module do
before do
# This is necessary because of the extra checks we have for the deprecated
# 'plugins' directory
- FileTest.stubs(:exist?).returns false
+ Puppet::FileSystem::File.stubs(:exist?).returns false
end
it "should have a class method that returns a named module from a given environment" do
@@ -90,12 +90,14 @@ describe Puppet::Module do
describe "when finding unmet dependencies" do
before do
- FileTest.unstub(:exist?)
+ Puppet::FileSystem::File.unstub(:exist?)
@modpath = tmpdir('modpath')
Puppet.settings[:modulepath] = @modpath
end
it "should list modules that are missing" do
+ metadata_file = "#{@modpath}/needy/metadata.json"
+ Puppet::FileSystem::File.expects(:exist?).twice.with(metadata_file).returns true
mod = PuppetSpec::Modules.create(
'needy',
@modpath,
@@ -116,6 +118,8 @@ describe Puppet::Module do
end
it "should list modules that are missing and have invalid names" do
+ metadata_file = "#{@modpath}/needy/metadata.json"
+ Puppet::FileSystem::File.expects(:exist?).with(metadata_file).twice.returns true
mod = PuppetSpec::Modules.create(
'needy',
@modpath,
@@ -136,6 +140,10 @@ describe Puppet::Module do
end
it "should list modules with unmet version requirement" do
+ ['foobar', 'foobaz'].each do |mod_name|
+ metadata_file = "#{@modpath}/#{mod_name}/metadata.json"
+ Puppet::FileSystem::File.stubs(:exist?).with(metadata_file).returns true
+ end
mod = PuppetSpec::Modules.create(
'foobar',
@modpath,
@@ -204,6 +212,8 @@ describe Puppet::Module do
end
it "should consider a dependency without a semantic version to be unmet" do
+ metadata_file = "#{@modpath}/foobar/metadata.json"
+ Puppet::FileSystem::File.expects(:exist?).with(metadata_file).times(3).returns true
mod = PuppetSpec::Modules.create(
'foobar',
@modpath,
@@ -244,6 +254,10 @@ describe Puppet::Module do
end
it "should only list unmet dependencies" do
+ [name, 'satisfied'].each do |mod_name|
+ metadata_file = "#{@modpath}/#{mod_name}/metadata.json"
+ Puppet::FileSystem::File.expects(:exist?).with(metadata_file).twice.returns true
+ end
mod = PuppetSpec::Modules.create(
name,
@modpath,
@@ -357,35 +371,42 @@ describe Puppet::Module do
end
end
- [:plugins, :templates, :files, :manifests].each do |filetype|
- dirname = filetype == :plugins ? "lib" : filetype.to_s
+ [:plugins, :pluginfacts, :templates, :files, :manifests].each do |filetype|
+ case filetype
+ when :plugins
+ dirname = "lib"
+ when :pluginfacts
+ dirname = "facts.d"
+ else
+ dirname = filetype.to_s
+ end
it "should be able to return individual #{filetype}" do
module_file = File.join(path, dirname, "my/file")
- FileTest.expects(:exist?).with(module_file).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(module_file).returns true
mod.send(filetype.to_s.sub(/s$/, ''), "my/file").should == module_file
end
it "should consider #{filetype} to be present if their base directory exists" do
module_file = File.join(path, dirname)
- FileTest.expects(:exist?).with(module_file).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(module_file).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
module_file = File.join(path, dirname)
- FileTest.expects(:exist?).with(module_file).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(module_file).returns false
mod.send(filetype.to_s + "?").should be_false
end
it "should return nil if asked to return individual #{filetype} that don't exist" do
module_file = File.join(path, dirname, "my/file")
- FileTest.expects(:exist?).with(module_file).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(module_file).returns false
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
base = File.join(path, dirname)
- FileTest.expects(:exist?).with(base).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(base).returns true
mod.send(filetype.to_s.sub(/s$/, ''), nil).should == base
end
end
@@ -418,8 +439,8 @@ describe Puppet::Module, "when finding matching manifests" do
end
it "should default to the 'init' file if no glob pattern is specified" do
- FileTest.expects(:exist?).with("/a/manifests/init.pp").returns(true)
- FileTest.expects(:exist?).with("/a/manifests/init.rb").returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with("/a/manifests/init.pp").returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with("/a/manifests/init.rb").returns(false)
@mod.match_manifests(nil).should == %w{/a/manifests/init.pp}
end
@@ -471,21 +492,21 @@ describe Puppet::Module do
end
it "should have metadata if it has a metadata file and its data is not empty" do
- FileTest.expects(:exist?).with(@module.metadata_file).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true
File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}"
@module.should be_has_metadata
end
it "should have metadata if it has a metadata file and its data is not empty" do
- FileTest.expects(:exist?).with(@module.metadata_file).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true
File.stubs(:read).with(@module.metadata_file).returns "{\"foo\" : \"bar\"}"
@module.should be_has_metadata
end
it "should not have metadata if has a metadata file and its data is empty" do
- FileTest.expects(:exist?).with(@module.metadata_file).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true
File.stubs(:read).with(@module.metadata_file).returns "/*
+-----------------------------------------------------------------------+
| |
@@ -503,7 +524,7 @@ describe Puppet::Module do
end
it "should know if it is missing a metadata file" do
- FileTest.expects(:exist?).with(@module.metadata_file).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns false
@module.should_not be_has_metadata
end
@@ -519,6 +540,13 @@ describe Puppet::Module do
Puppet::Module.new("yay", "/path", mock("env"))
end
+ it "should tolerate failure to parse" do
+ Puppet::FileSystem::File.expects(:exist?).with(@module.metadata_file).returns true
+ File.stubs(:read).with(@module.metadata_file).returns(my_fixture('trailing-comma.json'))
+
+ @module.has_metadata?.should be_false
+ end
+
def a_module_with_metadata(data)
text = data.to_pson
diff --git a/spec/unit/module_tool/tar/gnu_spec.rb b/spec/unit/module_tool/tar/gnu_spec.rb
index b5fc78918..93245da1a 100644
--- a/spec/unit/module_tool/tar/gnu_spec.rb
+++ b/spec/unit/module_tool/tar/gnu_spec.rb
@@ -8,9 +8,9 @@ describe Puppet::ModuleTool::Tar::Gnu do
let(:destfile) { '/the/dest/file.tar.gz' }
it "unpacks a tar file" do
- Puppet::Util::Execution.expects(:execute).with("tar xzf #{sourcefile} --no-same-permissions --no-same-owner -C #{destdir}")
+ Puppet::Util::Execution.expects(:execute).with("tar xzf #{sourcefile} --no-same-owner -C #{destdir}")
Puppet::Util::Execution.expects(:execute).with("find #{destdir} -type d -exec chmod 755 {} +")
- Puppet::Util::Execution.expects(:execute).with("find #{destdir} -type f -exec chmod 644 {} +")
+ Puppet::Util::Execution.expects(:execute).with("find #{destdir} -type f -exec chmod a-wst {} +")
Puppet::Util::Execution.expects(:execute).with("chown -R <owner:group> #{destdir}")
subject.unpack(sourcefile, destdir, '<owner:group>')
end
diff --git a/spec/unit/module_tool/tar/solaris_spec.rb b/spec/unit/module_tool/tar/solaris_spec.rb
index 23bbd8d20..1ca7a6a09 100644
--- a/spec/unit/module_tool/tar/solaris_spec.rb
+++ b/spec/unit/module_tool/tar/solaris_spec.rb
@@ -8,9 +8,9 @@ describe Puppet::ModuleTool::Tar::Solaris do
let(:destfile) { '/the/dest/file.tar.gz' }
it "unpacks a tar file" do
- Puppet::Util::Execution.expects(:execute).with("gtar xzf #{sourcefile} --no-same-permissions --no-same-owner -C #{destdir}")
+ Puppet::Util::Execution.expects(:execute).with("gtar xzf #{sourcefile} --no-same-owner -C #{destdir}")
Puppet::Util::Execution.expects(:execute).with("find #{destdir} -type d -exec chmod 755 {} +")
- Puppet::Util::Execution.expects(:execute).with("find #{destdir} -type f -exec chmod 644 {} +")
+ Puppet::Util::Execution.expects(:execute).with("find #{destdir} -type f -exec chmod a-wst {} +")
Puppet::Util::Execution.expects(:execute).with("chown -R <owner:group> #{destdir}")
subject.unpack(sourcefile, destdir, '<owner:group>')
end
diff --git a/spec/unit/module_tool/tar_spec.rb b/spec/unit/module_tool/tar_spec.rb
new file mode 100644
index 000000000..3de0dda48
--- /dev/null
+++ b/spec/unit/module_tool/tar_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+require 'puppet/module_tool/tar'
+
+describe Puppet::ModuleTool::Tar do
+
+ it "uses gtar when present on Solaris" do
+ Facter.stubs(:value).with('osfamily').returns 'Solaris'
+ Puppet::Util.stubs(:which).with('gtar').returns '/usr/bin/gtar'
+
+ described_class.instance(nil).should be_a_kind_of Puppet::ModuleTool::Tar::Solaris
+ end
+
+ it "uses gtar when present on OpenBSD" do
+ Facter.stubs(:value).with('osfamily').returns 'OpenBSD'
+ Puppet::Util.stubs(:which).with('gtar').returns '/usr/bin/gtar'
+
+ described_class.instance(nil).should be_a_kind_of Puppet::ModuleTool::Tar::Solaris
+ end
+
+ it "uses tar when present and not on Windows" do
+ Facter.stubs(:value).with('osfamily').returns 'ObscureLinuxDistro'
+ Puppet::Util.stubs(:which).with('tar').returns '/usr/bin/tar'
+ Puppet::Util::Platform.stubs(:windows?).returns false
+
+ described_class.instance(nil).should be_a_kind_of Puppet::ModuleTool::Tar::Gnu
+ end
+
+ it "falls back to minitar when it and zlib are present" do
+ Facter.stubs(:value).with('osfamily').returns 'Windows'
+ Puppet::Util.stubs(:which).with('tar')
+ Puppet::Util::Platform.stubs(:windows?).returns true
+ Puppet.stubs(:features).returns(stub(:minitar? => true, :zlib? => true))
+
+ described_class.instance(nil).should be_a_kind_of Puppet::ModuleTool::Tar::Mini
+ end
+
+ it "fails when there is no possible implementation" do
+ Facter.stubs(:value).with('osfamily').returns 'Windows'
+ Puppet::Util.stubs(:which).with('tar')
+ Puppet::Util::Platform.stubs(:windows?).returns true
+ Puppet.stubs(:features).returns(stub(:minitar? => false, :zlib? => false))
+
+ expect { described_class.instance(nil) }.to raise_error RuntimeError, /No suitable tar/
+ end
+end
diff --git a/spec/unit/network/authconfig_spec.rb b/spec/unit/network/authconfig_spec.rb
index 6814b2c1b..be45152c0 100755
--- a/spec/unit/network/authconfig_spec.rb
+++ b/spec/unit/network/authconfig_spec.rb
@@ -5,7 +5,8 @@ require 'puppet/network/authconfig'
describe Puppet::Network::AuthConfig do
before :each do
- File.stubs(:stat).returns(stub('stat', :ctime => :now))
+ stub_file = stub('file', :stat => stub('stat', :ctime => :now))
+ Puppet::FileSystem::File.stubs(:new).returns stub_file
Time.stubs(:now).returns Time.now
Puppet::Network::AuthConfig.any_instance.stubs(:exists?).returns(true)
diff --git a/spec/unit/network/authentication_spec.rb b/spec/unit/network/authentication_spec.rb
index 5b54757b8..c18552ab8 100755
--- a/spec/unit/network/authentication_spec.rb
+++ b/spec/unit/network/authentication_spec.rb
@@ -20,7 +20,7 @@ describe Puppet::Network::Authentication do
describe "when warning about upcoming expirations" do
before do
Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(false)
- FileTest.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
end
it "should check the expiration of the CA certificate" do
@@ -34,7 +34,7 @@ describe Puppet::Network::Authentication do
it "should check the expiration of the localhost certificate" do
Puppet::SSL::Host.stubs(:localhost).returns(host)
cert.expects(:near_expiration?).returns(false)
- FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(Puppet[:hostcert]).returns(true)
subject.warn_if_near_expiration
end
diff --git a/spec/unit/network/format_handler_spec.rb b/spec/unit/network/format_handler_spec.rb
index b8ab39634..1716a7296 100755
--- a/spec/unit/network/format_handler_spec.rb
+++ b/spec/unit/network/format_handler_spec.rb
@@ -63,8 +63,8 @@ describe Puppet::Network::FormatHandler do
Puppet::Network::FormatHandler.most_suitable_format_for(accepted, [:one, :two])
end
- it "finds either format when anything is accepted" do
- [format_one, format_two].should include(suitable_in_setup_formats(["*/*"]))
+ it "finds the most preferred format when anything is acceptable" do
+ Puppet::Network::FormatHandler.most_suitable_format_for(["*/*"], [:two, :one]).should == format_two
end
it "finds no format when none are acceptable" do
diff --git a/spec/unit/network/formats_spec.rb b/spec/unit/network/formats_spec.rb
index 8531a541a..57ff77795 100755
--- a/spec/unit/network/formats_spec.rb
+++ b/spec/unit/network/formats_spec.rb
@@ -26,6 +26,30 @@ class PsonTest
end
describe "Puppet Network Format" do
+ it "should include a msgpack format", :if => Puppet.features.msgpack? do
+ Puppet::Network::FormatHandler.format(:msgpack).should_not be_nil
+ end
+
+ describe "msgpack", :if => Puppet.features.msgpack? do
+ before do
+ @msgpack = Puppet::Network::FormatHandler.format(:msgpack)
+ end
+
+ it "should have its mime type set to application/x-msgpack" do
+ @msgpack.mime.should == "application/x-msgpack"
+ end
+
+ it "should have a weight of 20" do
+ @msgpack.weight.should == 20
+ end
+
+ it "should fail when one element does not have a from_pson" do
+ expect do
+ @msgpack.intern_multiple(Hash, MessagePack.pack(["foo"]))
+ end.to raise_error(NoMethodError)
+ end
+ end
+
it "should include a yaml format" do
Puppet::Network::FormatHandler.format(:yaml).should_not be_nil
end
diff --git a/spec/unit/network/http/connection_spec.rb b/spec/unit/network/http/connection_spec.rb
index 0d6da0671..467705f01 100644
--- a/spec/unit/network/http/connection_spec.rb
+++ b/spec/unit/network/http/connection_spec.rb
@@ -7,19 +7,13 @@ describe Puppet::Network::HTTP::Connection do
let (:host) { "me" }
let (:port) { 54321 }
- subject { Puppet::Network::HTTP::Connection.new(host, port) }
+ subject { Puppet::Network::HTTP::Connection.new(host, port, :verify => Puppet::SSL::Validator.no_validator) }
context "when providing HTTP connections" do
after do
Puppet::Network::HTTP::Connection.instance_variable_set("@ssl_host", nil)
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
- subject.send(:ssl_host).should equal(host)
- end
-
context "when initializing http instances" do
before :each do
# All of the cert stuff is tested elsewhere
@@ -39,51 +33,12 @@ describe Puppet::Network::HTTP::Connection do
end
it "can set ssl using an option" do
- Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => false).send(:connection).should_not be_use_ssl
- Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => true).send(:connection).should be_use_ssl
- end
-
- describe "peer verification" do
- def setup_standard_ssl_configuration
- ca_cert_file = File.expand_path('/path/to/ssl/certs/ca_cert.pem')
- FileTest.stubs(:exist?).with(ca_cert_file).returns(true)
-
- ssl_configuration = stub('ssl_configuration', :ca_auth_file => ca_cert_file)
- Puppet::Network::HTTP::Connection.any_instance.stubs(:ssl_configuration).returns(ssl_configuration)
- end
-
- def setup_standard_hostcert
- host_cert_file = File.expand_path('/path/to/ssl/certs/host_cert.pem')
- FileTest.stubs(:exist?).with(host_cert_file).returns(true)
-
- Puppet[:hostcert] = host_cert_file
- end
-
- def setup_standard_ssl_host
- cert = stub('cert', :content => 'real_cert')
- key = stub('key', :content => 'real_key')
- host = stub('host', :certificate => cert, :key => key, :ssl_store => stub('store'))
-
- Puppet::Network::HTTP::Connection.any_instance.stubs(:ssl_host).returns(host)
- end
-
- before do
- setup_standard_ssl_configuration
- setup_standard_hostcert
- setup_standard_ssl_host
- end
-
- it "can enable peer verification" do
- Puppet::Network::HTTP::Connection.new(host, port, :verify_peer => true).send(:connection).verify_mode.should == OpenSSL::SSL::VERIFY_PEER
- end
-
- it "can disable peer verification" do
- Puppet::Network::HTTP::Connection.new(host, port, :verify_peer => false).send(:connection).verify_mode.should == OpenSSL::SSL::VERIFY_NONE
- end
+ Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator).send(:connection).should_not be_use_ssl
+ Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => true, :verify => Puppet::SSL::Validator.no_validator).send(:connection).should be_use_ssl
end
context "proxy and timeout settings should propagate" do
- subject { Puppet::Network::HTTP::Connection.new(host, port).send(:connection) }
+ subject { Puppet::Network::HTTP::Connection.new(host, port, :verify => Puppet::SSL::Validator.no_validator).send(:connection) }
before :each do
Puppet[:http_proxy_host] = "myhost"
Puppet[:http_proxy_port] = 432
@@ -104,94 +59,13 @@ describe Puppet::Network::HTTP::Connection do
it "should raise Puppet::Error when invalid options are specified" do
expect { Puppet::Network::HTTP::Connection.new(host, port, :invalid_option => nil) }.to raise_error(Puppet::Error, 'Unrecognized option(s): :invalid_option')
end
-
- end
-
- describe "when doing SSL setup for http instances" do
- let :store do stub('store') end
-
- let :ca_auth_file do
- '/path/to/ssl/certs/ssl_server_ca_auth.pem'
- end
-
- let :ssl_configuration do
- stub('ssl_configuration', :ca_auth_file => ca_auth_file)
- end
-
- before :each do
- Puppet[:hostcert] = '/host/cert'
- Puppet::Network::HTTP::Connection.any_instance.stubs(:ssl_configuration).returns(ssl_configuration)
-
- cert = stub 'cert', :content => 'real_cert'
- key = stub 'key', :content => 'real_key'
- host = stub 'host', :certificate => cert, :key => key, :ssl_store => store
- Puppet::Network::HTTP::Connection.any_instance.stubs(:ssl_host).returns(host)
- end
-
- shared_examples "HTTPS setup without all certificates" do
- subject { Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => true).send(:connection) }
-
- it { should be_use_ssl }
- its(:cert) { should be_nil }
- its(:ca_file) { should be_nil }
- its(:key) { should be_nil }
- its(:verify_mode) { should == OpenSSL::SSL::VERIFY_NONE }
- end
-
- context "with neither a host cert or a local CA cert" do
- before :each do
- FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(false)
- FileTest.stubs(:exist?).with(ca_auth_file).returns(false)
- end
-
- include_examples "HTTPS setup without all certificates"
- end
-
- context "with there is no host certificate" do
- before :each do
- FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(false)
- FileTest.stubs(:exist?).with(ca_auth_file).returns(true)
- end
-
- include_examples "HTTPS setup without all certificates"
- end
-
- context "with there is no local CA certificate" do
- before :each do
- FileTest.stubs(:exist?).with(Puppet[:hostcert]).returns(true)
- FileTest.stubs(:exist?).with(ca_auth_file).returns(false)
- end
-
- include_examples "HTTPS setup without all certificates"
- end
-
- context "with both the host and CA cert" do
- subject { Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => true).send(:connection) }
-
- before :each do
- FileTest.expects(:exist?).with(Puppet[:hostcert]).returns(true)
- FileTest.expects(:exist?).with(ca_auth_file).returns(true)
- end
-
- it { should be_use_ssl }
- its(:cert_store) { should equal store }
- its(:cert) { should == "real_cert" }
- its(:key) { should == "real_key" }
- its(:verify_mode) { should == OpenSSL::SSL::VERIFY_PEER }
- its(:ca_file) { should == ca_auth_file }
- end
-
- it "should set up certificate information when creating http instances" do
- subject.expects(:cert_setup)
- subject.send(:connection)
- end
end
end
context "when methods that accept a block are called with a block" do
let (:host) { "my_server" }
let (:port) { 8140 }
- let (:subject) { Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => false) }
+ let (:subject) { Puppet::Network::HTTP::Connection.new(host, port, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator) }
let (:httpok) { Net::HTTPOK.new('1.1', 200, '') }
before :each do
@@ -228,95 +102,99 @@ describe Puppet::Network::HTTP::Connection do
let (:host) { "my_server" }
let (:port) { 8140 }
let (:httpok) { Net::HTTPOK.new('1.1', 200, '') }
- let (:subject) { Puppet::Network::HTTP::Connection.new(host, port) }
-
- def a_connection_that_verifies(args)
- connection = Net::HTTP.new(host, port)
- connection.stubs(:warn_if_near_expiration)
- connection.stubs(:get).with do
- connection.verify_callback.call(args[:has_passed_pre_checks], args[:in_context])
- true
- end.raises(OpenSSL::SSL::SSLError.new(args[:fails_with]))
- connection
- end
-
- def a_store_context(args)
- Puppet[:confdir] = tmpdir('conf')
- ssl_context = mock('OpenSSL::X509::StoreContext')
- if args[:verify_raises]
- ssl_context.stubs(:current_cert).raises("oh noes")
- else
- cert = Puppet::SSL::CertificateAuthority.new.generate(args[:for_server], :dns_alt_names => args[:for_aliases]).content
- ssl_context.stubs(:current_cert).returns(cert)
- end
- ssl_context.stubs(:chain).returns([])
- ssl_context.stubs(:error_string).returns(args[:with_error_string])
- ssl_context
- end
it "should provide a useful error message when one is available and certificate validation fails", :unless => Puppet.features.microsoft_windows? do
- subject.stubs(:create_connection).
- returns(a_connection_that_verifies(:has_passed_pre_checks => false,
- :in_context => a_store_context(:for_server => 'not_my_server',
- :with_error_string => 'shady looking signature'),
- :fails_with => 'certificate verify failed'))
- expect do
- subject.request(:get, stub('request'))
- end.to raise_error(Puppet::Error, "certificate verify failed: [shady looking signature for /CN=not_my_server]")
- end
+ connection = Puppet::Network::HTTP::Connection.new(
+ host, port,
+ :verify => ConstantErrorValidator.new(:fails_with => 'certificate verify failed',
+ :error_string => 'shady looking signature'))
- it "should provide a useful error message when verify_callback raises", :unless => Puppet.features.microsoft_windows? do
- subject.stubs(:create_connection).
- returns(a_connection_that_verifies(:has_passed_pre_checks => false,
- :in_context => a_store_context(:verify_raises => true),
- :fails_with => 'certificate verify failed'))
expect do
- subject.request(:get, stub('request'))
- end.to raise_error(Puppet::Error, "certificate verify failed: [oh noes]")
+ connection.get('request')
+ end.to raise_error(Puppet::Error, "certificate verify failed: [shady looking signature]")
end
it "should provide a helpful error message when hostname was not match with server certificate", :unless => Puppet.features.microsoft_windows? do
- subject.stubs(:create_connection).
- returns(a_connection_that_verifies(:has_passed_pre_checks => true,
- :in_context => a_store_context(:for_server => 'not_my_server',
- :for_aliases => 'foo,bar,baz'),
- :fails_with => 'hostname was not match with server certificate'))
-
- expect { subject.request(:get, stub('request')) }.
- to raise_error(Puppet::Error) do |error|
+ Puppet[:confdir] = tmpdir('conf')
+
+ connection = Puppet::Network::HTTP::Connection.new(
+ host, port,
+ :verify => ConstantErrorValidator.new(
+ :fails_with => 'hostname was not match with server certificate',
+ :peer_certs => [Puppet::SSL::CertificateAuthority.new.generate(
+ 'not_my_server', :dns_alt_names => 'foo,bar,baz')]))
+
+ expect do
+ connection.get('request')
+ end.to raise_error(Puppet::Error) do |error|
error.message =~ /Server hostname 'my_server' did not match server certificate; expected one of (.+)/
$1.split(', ').should =~ %w[DNS:foo DNS:bar DNS:baz DNS:not_my_server not_my_server]
end
end
it "should pass along the error message otherwise" do
- connection = Net::HTTP.new('my_server', 8140)
- subject.stubs(:create_connection).returns(connection)
-
- connection.stubs(:get).raises(OpenSSL::SSL::SSLError.new('some other message'))
+ connection = Puppet::Network::HTTP::Connection.new(
+ host, port,
+ :verify => ConstantErrorValidator.new(:fails_with => 'some other message'))
expect do
- subject.request(:get, stub('request'))
+ connection.get('request')
end.to raise_error(/some other message/)
end
it "should check all peer certificates for upcoming expiration", :unless => Puppet.features.microsoft_windows? do
- connection = Net::HTTP.new('my_server', 8140)
- subject.stubs(:create_connection).returns(connection)
+ Puppet[:confdir] = tmpdir('conf')
+ cert = Puppet::SSL::CertificateAuthority.new.generate(
+ 'server', :dns_alt_names => 'foo,bar,baz')
+
+ connection = Puppet::Network::HTTP::Connection.new(
+ host, port,
+ :verify => NoProblemsValidator.new(cert))
- cert = stubs 'cert'
- Puppet::SSL::Certificate.expects(:from_instance).twice.returns(cert)
+ Net::HTTP.any_instance.stubs(:get).returns(httpok)
- connection.stubs(:get).with do
- context = a_store_context(:for_server => 'a_server', :with_error_string => false)
- connection.verify_callback.call(true, context)
- connection.verify_callback.call(true, context)
- true
- end.returns(httpok)
+ connection.expects(:warn_if_near_expiration).with(cert)
- subject.expects(:warn_if_near_expiration).with(cert, cert)
+ connection.get('request')
+ end
+
+ class ConstantErrorValidator
+ def initialize(args)
+ @fails_with = args[:fails_with]
+ @error_string = args[:error_string] || ""
+ @peer_certs = args[:peer_certs] || []
+ end
- subject.request(:get, stubs('request'))
+ def setup_connection(connection)
+ connection.stubs(:get).with do
+ true
+ end.raises(OpenSSL::SSL::SSLError.new(@fails_with))
+ end
+
+ def peer_certs
+ @peer_certs
+ end
+
+ def verify_errors
+ [@error_string]
+ end
+ end
+
+ class NoProblemsValidator
+ def initialize(cert)
+ @cert = cert
+ end
+
+ def setup_connection(connection)
+ end
+
+ def peer_certs
+ [@cert]
+ end
+
+ def verify_errors
+ []
+ end
end
end
@@ -324,7 +202,7 @@ describe Puppet::Network::HTTP::Connection do
let (:other_host) { "redirected" }
let (:other_port) { 9292 }
let (:other_path) { "other-path" }
- let (:subject) { Puppet::Network::HTTP::Connection.new("my_server", 8140, :use_ssl => false) }
+ let (:subject) { Puppet::Network::HTTP::Connection.new("my_server", 8140, :use_ssl => false, :verify => Puppet::SSL::Validator.no_validator) }
let (:httpredirection) { Net::HTTPFound.new('1.1', 302, 'Moved Temporarily') }
let (:httpok) { Net::HTTPOK.new('1.1', 200, '') }
@@ -356,5 +234,4 @@ describe Puppet::Network::HTTP::Connection do
}.to raise_error(Puppet::Network::HTTP::RedirectionLimitExceededException)
end
end
-
end
diff --git a/spec/unit/network/http/handler_spec.rb b/spec/unit/network/http/handler_spec.rb
index 0ff087b02..53da5be01 100755
--- a/spec/unit/network/http/handler_spec.rb
+++ b/spec/unit/network/http/handler_spec.rb
@@ -38,6 +38,9 @@ describe Puppet::Network::HTTP::Handler do
end
class Puppet::TestModel::Memory < Puppet::Indirector::Memory
+ def supports_remote_requests?
+ true
+ end
end
Puppet::TestModel.indirection.terminus_class = :memory
@@ -150,9 +153,7 @@ describe Puppet::Network::HTTP::Handler do
describe "when processing a request" do
let(:response) do
- obj = stub "http 200 ok"
- obj.stubs(:[]=).with(Puppet::Network::HTTP::HEADER_PUPPET_VERSION, Puppet.version)
- obj
+ { :status => 200 }
end
before do
@@ -163,7 +164,6 @@ describe Puppet::Network::HTTP::Handler do
it "should check the client certificate for upcoming expiration" do
request = a_request
cert = mock 'cert'
- handler.stubs(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}])
handler.expects(:client_cert).returns(cert).with(request)
handler.expects(:warn_if_near_expiration).with(cert)
@@ -201,33 +201,31 @@ describe Puppet::Network::HTTP::Handler do
handler.process(request, response)
end
- it "should call the 'do' method and delegate authorization to the authorization layer" do
+ it "should return 403 if the request is not authorized" do
request = a_request
handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}])
- handler.expects(:do_mymethod).with("facts", "key", {:node => "name"}, request, response)
+ handler.expects(:do_mymethod).never
+
+ handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden"))
- handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"})
+ handler.expects(:set_response).with(anything, anything, 403)
handler.process(request, response)
end
- it "should return 403 if the request is not authorized" do
+ it "should return an error code if the indirection does not support remote requests" do
request = a_request
- handler.expects(:uri2indirection).returns(["facts", :mymethod, "key", {:node => "name"}])
- handler.expects(:do_mymethod).never
-
- handler.expects(:check_authorization).with("facts", :mymethod, "key", {:node => "name"}).raises(Puppet::Network::AuthorizationError.new("forbidden"))
-
- handler.expects(:set_response).with(anything, anything, 403)
+ indirection.expects(:allow_remote_requests?).returns(false)
handler.process(request, response)
+
+ expect(response[:status]).to eq 404
end
it "should serialize a controller exception when an exception is thrown while finding the model instance" do
- request = a_request
- handler.expects(:uri2indirection).returns(["facts", :find, "key", {:node => "name"}])
+ request = a_request_that_finds(Puppet::TestModel.new("key"))
handler.expects(:do_find).raises(ArgumentError, "The exception")
handler.expects(:set_response).with(anything, "The exception", 400)
@@ -302,7 +300,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, data.render(:pson))
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson))
- handler.do_find(indirection.name, "my data", {}, request, response)
+ handler.do_find(indirection, "my data", {}, request, response)
end
it "responds with a 406 error when no accept header is provided" do
@@ -311,7 +309,7 @@ describe Puppet::Network::HTTP::Handler do
request = a_request_that_finds(data, :accept_header => nil)
expect do
- handler.do_find(indirection.name, "my data", {}, request, response)
+ handler.do_find(indirection, "my data", {}, request, response)
end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError)
end
@@ -321,7 +319,7 @@ describe Puppet::Network::HTTP::Handler do
request = a_request_that_finds(data, :accept_header => "unknown, also/unknown")
expect do
- handler.do_find(indirection.name, "my data", {}, request, response)
+ handler.do_find(indirection, "my data", {}, request, response)
end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError)
end
@@ -334,7 +332,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, data_string)
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson))
- handler.do_find(indirection.name, "my data", {}, request, response)
+ handler.do_find(indirection, "my data", {}, request, response)
end
it "should return a 404 when no model instance can be found" do
@@ -342,7 +340,7 @@ describe Puppet::Network::HTTP::Handler do
request = a_request_that_finds(data, :accept_header => "unknown, pson, yaml")
expect do
- handler.do_find(indirection.name, "my data", {}, request, response)
+ handler.do_find(indirection, "my data", {}, request, response)
end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotFoundError)
end
end
@@ -377,7 +375,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, Puppet::TestModel.render_multiple(:pson, [data]))
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson))
- handler.do_search(indirection.name, "my", {}, request, response)
+ handler.do_search(indirection, "my", {}, request, response)
end
it "should return [] when searching returns an empty array" do
@@ -386,7 +384,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, Puppet::TestModel.render_multiple(:pson, []))
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson))
- handler.do_search(indirection.name, "nothing", {}, request, response)
+ handler.do_search(indirection, "nothing", {}, request, response)
end
it "should return a 404 when searching returns nil" do
@@ -394,7 +392,7 @@ describe Puppet::Network::HTTP::Handler do
indirection.expects(:search).returns(nil)
expect do
- handler.do_search(indirection.name, "nothing", {}, request, response)
+ handler.do_search(indirection, "nothing", {}, request, response)
end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotFoundError)
end
end
@@ -405,7 +403,7 @@ describe Puppet::Network::HTTP::Handler do
indirection.save(data, "my data")
request = a_request_that_destroys(data)
- handler.do_destroy(indirection.name, "my data", {}, request, response)
+ handler.do_destroy(indirection, "my data", {}, request, response)
Puppet::TestModel.indirection.find("my data").should be_nil
end
@@ -418,7 +416,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, data.render(:yaml))
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:yaml))
- handler.do_destroy(indirection.name, "my data", {}, request, response)
+ handler.do_destroy(indirection, "my data", {}, request, response)
end
it "uses the first supported format for the response" do
@@ -429,7 +427,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, data.render(:pson))
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson))
- handler.do_destroy(indirection.name, "my data", {}, request, response)
+ handler.do_destroy(indirection, "my data", {}, request, response)
end
it "raises an error and does not destory when no accepted formats are known" do
@@ -438,7 +436,7 @@ describe Puppet::Network::HTTP::Handler do
request = a_request_that_submits(data, :accept_header => "unknown, also/unknown")
expect do
- handler.do_destroy(indirection.name, "my data", {}, request, response)
+ handler.do_destroy(indirection, "my data", {}, request, response)
end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError)
Puppet::TestModel.indirection.find("my data").should_not be_nil
@@ -460,7 +458,7 @@ describe Puppet::Network::HTTP::Handler do
request[:content_type_header] = "application/x-raw"
request[:body] = ''
- handler.do_save(indirection.name, "test", {}, request, response)
+ handler.do_save(indirection, "test", {}, request, response)
Puppet::TestModel.indirection.find("test").data.should == ''
end
@@ -469,7 +467,7 @@ describe Puppet::Network::HTTP::Handler do
data = Puppet::TestModel.new("my data", "some data")
request = a_request_that_submits(data)
- handler.do_save(indirection.name, "my data", {}, request, response)
+ handler.do_save(indirection, "my data", {}, request, response)
Puppet::TestModel.indirection.find("my data").should == data
end
@@ -481,7 +479,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, data.render(:yaml))
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:yaml))
- handler.do_save(indirection.name, "my data", {}, request, response)
+ handler.do_save(indirection, "my data", {}, request, response)
end
it "uses the first supported format for the response" do
@@ -491,7 +489,7 @@ describe Puppet::Network::HTTP::Handler do
handler.expects(:set_response).with(response, data.render(:pson))
handler.expects(:set_content_type).with(response, Puppet::Network::FormatHandler.format(:pson))
- handler.do_save(indirection.name, "my data", {}, request, response)
+ handler.do_save(indirection, "my data", {}, request, response)
end
it "raises an error and does not save when no accepted formats are known" do
@@ -499,7 +497,7 @@ describe Puppet::Network::HTTP::Handler do
request = a_request_that_submits(data, :accept_header => "unknown, also/unknown")
expect do
- handler.do_save(indirection.name, "my data", {}, request, response)
+ handler.do_save(indirection, "my data", {}, request, response)
end.to raise_error(Puppet::Network::HTTP::Handler::HTTPNotAcceptableError)
Puppet::TestModel.indirection.find("my data").should be_nil
@@ -543,7 +541,8 @@ describe Puppet::Network::HTTP::Handler do
end
def set_response(response, body, status = 200)
- "my_result"
+ response[:body] = body
+ response[:status] = status
end
def http_method(request)
diff --git a/spec/unit/network/http_pool_spec.rb b/spec/unit/network/http_pool_spec.rb
index c671e88f1..4ee507568 100755
--- a/spec/unit/network/http_pool_spec.rb
+++ b/spec/unit/network/http_pool_spec.rb
@@ -3,6 +3,10 @@ require 'spec_helper'
require 'puppet/network/http_pool'
describe Puppet::Network::HttpPool do
+ before :each do
+ Puppet::SSL::Key.indirection.terminus_class = :memory
+ Puppet::SSL::CertificateRequest.indirection.terminus_class = :memory
+ end
describe "when managing http instances" do
@@ -26,15 +30,14 @@ describe Puppet::Network::HttpPool do
describe 'peer verification' do
def setup_standard_ssl_configuration
ca_cert_file = File.expand_path('/path/to/ssl/certs/ca_cert.pem')
- FileTest.stubs(:exist?).with(ca_cert_file).returns(true)
- ssl_configuration = stub('ssl_configuration', :ca_auth_file => ca_cert_file)
- Puppet::Network::HTTP::Connection.any_instance.stubs(:ssl_configuration).returns(ssl_configuration)
+ Puppet[:ssl_client_ca_auth] = ca_cert_file
+ Puppet::FileSystem::File.stubs(:exist?).with(ca_cert_file).returns(true)
end
def setup_standard_hostcert
host_cert_file = File.expand_path('/path/to/ssl/certs/host_cert.pem')
- FileTest.stubs(:exist?).with(host_cert_file).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(host_cert_file).returns(true)
Puppet[:hostcert] = host_cert_file
end
@@ -44,7 +47,7 @@ describe Puppet::Network::HttpPool do
key = stub('key', :content => 'real_key')
host = stub('host', :certificate => cert, :key => key, :ssl_store => stub('store'))
- Puppet::Network::HTTP::Connection.any_instance.stubs(:ssl_host).returns(host)
+ Puppet::SSL::Host.stubs(:localhost).returns(host)
end
before do
diff --git a/spec/unit/node/environment_spec.rb b/spec/unit/node/environment_spec.rb
index 7b2d73b8a..be8ae1a94 100755
--- a/spec/unit/node/environment_spec.rb
+++ b/spec/unit/node/environment_spec.rb
@@ -20,111 +20,97 @@ describe Puppet::Node::Environment do
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 default environment if no name is provided while initializing an environment" do
Puppet[:environment] = "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
@collection = Puppet::Resource::TypeCollection.new(env)
env.stubs(:perform_initial_import).returns(Puppet::Parser::AST::Hostclass.new(''))
- Thread.current[:known_resource_types] = nil
+ $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.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
-
+ $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
+ $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
-
+
it "should validate the modulepath directories" do
real_file = tmpdir('moduledir')
path = %W[/one /two #{real_file}].join(File::PATH_SEPARATOR)
-
+
Puppet[:modulepath] = path
-
+
env.modulepath.should == [real_file]
end
-
+
it "should prefix the value of the 'PUPPETLIB' environment variable to the module path if present" do
Puppet::Util.withenv("PUPPETLIB" => %w{/l1 /l2}.join(File::PATH_SEPARATOR)) do
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 = tmpdir("path_one")
@@ -132,69 +118,69 @@ describe Puppet::Node::Environment do
sep = File::PATH_SEPARATOR
Puppet[:modulepath] = "#{@path_one}#{sep}#{@path_two}"
end
-
+
it "should not return non-directories" do
FileTest.expects(:directory?).with(@path_one).returns true
FileTest.expects(:directory?).with(@path_two).returns false
-
+
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
-
two = File.expand_path("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.should respond_to(:[])
end
-
+
it "should ask the Puppet settings instance for the setting qualified with the environment name" do
Puppet.settings.set_value(:server, "myval", :testing)
env[:server].should == "myval"
end
-
+
it "should be able to return an individual module that exists in its module path" do
env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))]
-
+
mod = env.module('one')
mod.should be_a(Puppet::Module)
mod.name.should == 'one'
end
-
+
it "should not return a module if the module doesn't exist" do
env.stubs(:modules).returns [Puppet::Module.new('one', "/one", mock("env"))]
-
+
env.module('two').should be_nil
end
-
+
it "should return nil if asked for a module that does not exist in its path" do
modpath = tmpdir('modpath')
env.modulepath = [modpath]
-
+
env.module("one").should be_nil
end
-
+
describe "module data" do
before do
dir = tmpdir("deep_path")
-
+
@first = File.join(dir, "first")
@second = File.join(dir, "second")
Puppet[:modulepath] = "#{@first}#{File::PATH_SEPARATOR}#{@second}"
-
+
FileUtils.mkdir_p(@first)
FileUtils.mkdir_p(@second)
end
-
+
describe "#modules_by_path" do
it "should return an empty list if there are no modules" do
env.modules_by_path.should == {
@@ -202,19 +188,19 @@ describe Puppet::Node::Environment do
@second => []
}
end
-
+
it "should include modules even if they exist in multiple dirs in the modulepath" do
modpath1 = File.join(@first, "foo")
FileUtils.mkdir_p(modpath1)
modpath2 = File.join(@second, "foo")
FileUtils.mkdir_p(modpath2)
-
+
env.modules_by_path.should == {
@first => [Puppet::Module.new('foo', modpath1, env)],
@second => [Puppet::Module.new('foo', modpath2, env)]
}
end
-
+
it "should ignore modules with invalid names" do
FileUtils.mkdir_p(File.join(@first, 'foo'))
FileUtils.mkdir_p(File.join(@first, 'foo2'))
@@ -226,12 +212,12 @@ describe Puppet::Node::Environment do
FileUtils.mkdir_p(File.join(@first, '-foo'))
FileUtils.mkdir_p(File.join(@first, 'foo-'))
FileUtils.mkdir_p(File.join(@first, 'foo--bar'))
-
+
env.modules_by_path[@first].collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar}
end
-
+
end
-
+
describe "#module_requirements" do
it "should return a list of what modules depend on other modules" do
PuppetSpec::Modules.create(
@@ -266,7 +252,7 @@ describe Puppet::Node::Environment do
:dependencies => [{ 'name' => 'puppetlabs/bar', "version_requirement" => "~3.0.0" }]
}
)
-
+
env.module_requirements.should == {
'puppetlabs/alpha' => [],
'puppetlabs/foo' => [
@@ -297,7 +283,7 @@ describe Puppet::Node::Environment do
}
end
end
-
+
describe ".module_by_forge_name" do
it "should find modules by forge_name" do
mod = PuppetSpec::Modules.create(
@@ -308,7 +294,7 @@ describe Puppet::Node::Environment do
)
env.module_by_forge_name('puppetlabs/baz').should == mod
end
-
+
it "should not find modules with same name by the wrong author" do
mod = PuppetSpec::Modules.create(
'baz',
@@ -318,17 +304,17 @@ describe Puppet::Node::Environment do
)
env.module_by_forge_name('puppetlabs/baz').should == nil
end
-
+
it "should return nil when the module can't be found" do
env.module_by_forge_name('ima/nothere').should be_nil
end
end
-
+
describe ".modules" do
it "should return an empty list if there are no modules" do
env.modules.should == []
end
-
+
it "should return a module named for every directory in each module path" do
%w{foo bar}.each do |mod_name|
FileUtils.mkdir_p(File.join(@first, mod_name))
@@ -338,14 +324,14 @@ describe Puppet::Node::Environment do
end
env.modules.collect{|mod| mod.name}.sort.should == %w{foo bar bee baz}.sort
end
-
+
it "should remove duplicates" do
FileUtils.mkdir_p(File.join(@first, 'foo'))
FileUtils.mkdir_p(File.join(@second, 'foo'))
-
+
env.modules.collect{|mod| mod.name}.sort.should == %w{foo}
end
-
+
it "should ignore modules with invalid names" do
FileUtils.mkdir_p(File.join(@first, 'foo'))
FileUtils.mkdir_p(File.join(@first, 'foo2'))
@@ -353,63 +339,63 @@ describe Puppet::Node::Environment do
FileUtils.mkdir_p(File.join(@first, 'foo_bar'))
FileUtils.mkdir_p(File.join(@first, 'foo=bar'))
FileUtils.mkdir_p(File.join(@first, 'foo bar'))
-
+
env.modules.collect{|mod| mod.name}.sort.should == %w{foo foo-bar foo2 foo_bar}
end
-
+
it "should create modules with the correct environment" do
FileUtils.mkdir_p(File.join(@first, 'foo'))
env.modules.each {|mod| mod.environment.should == env }
end
-
+
end
end
-
+
it "should cache the module list" do
env.modulepath = %w{/a}
Dir.expects(:entries).once.with("/a").returns %w{foo}
-
+
env.modules
env.modules
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 as a symbol" do
@helper.environment = :foo
@helper.environment.name.should == :foo
end
-
+
it "should accept an environment directly" do
@helper.environment = Puppet::Node::Environment.new(:foo)
@helper.environment.name.should == :foo
end
-
+
it "should accept an environment as a string" do
@helper.environment = 'foo'
@helper.environment.name.should == :foo
end
end
-
+
describe "when performing initial import" do
before do
@parser = Puppet::Parser::ParserFactory.parser("test")
# @parser = Puppet::Parser::EParserAdapter.new(Puppet::Parser::Parser.new("test")) # TODO: FIX PARSER FACTORY
Puppet::Parser::ParserFactory.stubs(:parser).returns @parser
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| }
@@ -418,7 +404,7 @@ describe Puppet::Node::Environment do
@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] = ""
@@ -427,15 +413,15 @@ describe Puppet::Node::Environment do
@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
+ Puppet::FileSystem::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
@@ -443,11 +429,11 @@ describe Puppet::Node::Environment do
@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
@@ -465,5 +451,5 @@ describe Puppet::Node::Environment do
end
it_behaves_like 'the environment'
end
-
+
end
diff --git a/spec/unit/node/facts_spec.rb b/spec/unit/node/facts_spec.rb
index 9ba6baa11..55e3dbbf6 100755
--- a/spec/unit/node/facts_spec.rb
+++ b/spec/unit/node/facts_spec.rb
@@ -1,8 +1,17 @@
#! /usr/bin/env ruby
require 'spec_helper'
-require 'matchers/json'
require 'puppet/node/facts'
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ describe "catalog facts schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, FACTS_SCHEMA)
+ end
+ end
+
+end
+
describe Puppet::Node::Facts, "when indirecting" do
before do
@facts = Puppet::Node::Facts.new("me")
@@ -143,8 +152,16 @@ describe Puppet::Node::Facts, "when indirecting" do
result = PSON.parse(facts.to_pson)
result['name'].should == facts.name
result['values'].should == facts.values.reject { |key, value| key.to_s =~ /_/ }
- result['timestamp'].should == facts.timestamp.to_s
- result['expiration'].should == facts.expiration.to_s
+ result['timestamp'].should == facts.timestamp.iso8601(9)
+ result['expiration'].should == facts.expiration.iso8601(9)
+ end
+
+ it "should generate valid facts data against the facts schema", :unless => Puppet.features.microsoft_windows? do
+ Time.stubs(:now).returns(@timestamp)
+ facts = Puppet::Node::Facts.new("foo", {'a' => 1, 'b' => 2, 'c' => 3})
+ facts.expiration = @expiration
+
+ JSON::Validator.validate!(FACTS_SCHEMA, facts.to_pson)
end
it "should not include nil values" do
diff --git a/spec/unit/node_spec.rb b/spec/unit/node_spec.rb
index 5c36149ad..f8691ed9d 100755
--- a/spec/unit/node_spec.rb
+++ b/spec/unit/node_spec.rb
@@ -2,6 +2,17 @@
require 'spec_helper'
require 'matchers/json'
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ NODE_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../api/schemas/node.json')))
+
+ describe "node schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, NODE_SCHEMA)
+ end
+ end
+end
+
describe Puppet::Node do
it "should register its document type as Node" do
PSON.registered_document_types["Node"].should equal(Puppet::Node)
@@ -55,6 +66,38 @@ describe Puppet::Node do
new_node.name.should == node.name
end
+ it "can round-trip through pson" do
+ facts = Puppet::Node::Facts.new("hello", "one" => "c", "two" => "b")
+ node = Puppet::Node.new("hello",
+ :environment => 'kjhgrg',
+ :classes => ['erth', 'aiu'],
+ :parameters => {"hostname"=>"food"}
+ )
+ new_node = Puppet::Node.convert_from('pson', node.render('pson'))
+ new_node.environment.should == node.environment
+ new_node.parameters.should == node.parameters
+ new_node.classes.should == node.classes
+ new_node.name.should == node.name
+ end
+
+ it "validates against the node json schema", :unless => Puppet.features.microsoft_windows? do
+ facts = Puppet::Node::Facts.new("hello", "one" => "c", "two" => "b")
+ node = Puppet::Node.new("hello",
+ :environment => 'kjhgrg',
+ :classes => ['erth', 'aiu'],
+ :parameters => {"hostname"=>"food"}
+ )
+ JSON::Validator.validate!(NODE_SCHEMA, node.to_pson)
+ end
+
+ it "when missing optional parameters validates against the node json schema", :unless => Puppet.features.microsoft_windows? do
+ facts = Puppet::Node::Facts.new("hello", "one" => "c", "two" => "b")
+ node = Puppet::Node.new("hello",
+ :environment => 'kjhgrg'
+ )
+ JSON::Validator.validate!(NODE_SCHEMA, node.to_pson)
+ end
+
describe "when converting to json" do
before do
@node = Puppet::Node.new("mynode")
diff --git a/spec/unit/parameter/boolean_spec.rb b/spec/unit/parameter/boolean_spec.rb
index 7039c42fc..505bc561f 100644
--- a/spec/unit/parameter/boolean_spec.rb
+++ b/spec/unit/parameter/boolean_spec.rb
@@ -5,21 +5,31 @@ require 'puppet/parameter/boolean'
describe Puppet::Parameter::Boolean do
let (:resource) { mock('resource') }
- subject { described_class.new(:resource => resource) }
-
- [ true, :true, 'true', :yes, 'yes', 'TrUe', 'yEs' ].each do |arg|
- it "should munge #{arg.inspect} as true" do
- subject.munge(arg).should == true
+ describe "after initvars" do
+ before { described_class.initvars }
+ it "should have the correct value_collection" do
+ described_class.value_collection.values.sort.should ==
+ [:true, :false, :yes, :no].sort
end
end
- [ false, :false, 'false', :no, 'no', 'FaLSE', 'nO' ].each do |arg|
- it "should munge #{arg.inspect} as false" do
- subject.munge(arg).should == false
+
+ describe "instances" do
+ subject { described_class.new(:resource => resource) }
+
+ [ true, :true, 'true', :yes, 'yes', 'TrUe', 'yEs' ].each do |arg|
+ it "should munge #{arg.inspect} as true" do
+ subject.munge(arg).should == true
+ end
end
- end
- [ nil, :undef, 'undef', '0', 0, '1', 1, 9284 ].each do |arg|
- it "should fail to munge #{arg.inspect}" do
- expect { subject.munge(arg) }.to raise_error Puppet::Error
+ [ false, :false, 'false', :no, 'no', 'FaLSE', 'nO' ].each do |arg|
+ it "should munge #{arg.inspect} as false" do
+ subject.munge(arg).should == false
+ end
+ end
+ [ nil, :undef, 'undef', '0', 0, '1', 1, 9284 ].each do |arg|
+ it "should fail to munge #{arg.inspect}" do
+ expect { subject.munge(arg) }.to raise_error Puppet::Error
+ end
end
end
end
diff --git a/spec/unit/parser/ast/resourceparam_spec.rb b/spec/unit/parser/ast/resourceparam_spec.rb
new file mode 100644
index 000000000..818f146d3
--- /dev/null
+++ b/spec/unit/parser/ast/resourceparam_spec.rb
@@ -0,0 +1,51 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+describe Puppet::Parser::AST::ResourceParam do
+
+ ast = Puppet::Parser::AST
+
+ before :each do
+ @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("mynode"))
+ @scope = Puppet::Parser::Scope.new(@compiler)
+ @params = ast::ASTArray.new({})
+ @compiler.stubs(:add_override)
+ end
+
+ it "should evaluate the parameter value" do
+ object = mock 'object'
+ object.expects(:safeevaluate).with(@scope).returns('value')
+ ast::ResourceParam.new(:param => 'myparam', :value => object).evaluate(@scope)
+ end
+
+ it "should return a Puppet::Parser::Resource::Param on evaluation" do
+ object = mock 'object'
+ object.expects(:safeevaluate).with(@scope).returns('value')
+ evaled = ast::ResourceParam.new(:param => 'myparam', :value => object).evaluate(@scope)
+ evaled.should be_a(Puppet::Parser::Resource::Param)
+ evaled.name.to_s.should == 'myparam'
+ evaled.value.to_s.should == 'value'
+ end
+
+ it "should copy line numbers to Puppet::Parser::Resource::Param" do
+ object = mock 'object'
+ object.expects(:safeevaluate).with(@scope).returns('value')
+ evaled = ast::ResourceParam.new(:param => 'myparam', :value => object, :line => 42).evaluate(@scope)
+ evaled.line.should == 42
+ end
+
+ it "should copy source file to Puppet::Parser::Resource::Param" do
+ object = mock 'object'
+ object.expects(:safeevaluate).with(@scope).returns('value')
+ evaled = ast::ResourceParam.new(:param => 'myparam', :value => object, :file => 'foo.pp').evaluate(@scope)
+ evaled.file.should == 'foo.pp'
+ end
+
+ it "should change nil parameter values to undef" do
+ object = mock 'object'
+ object.expects(:safeevaluate).with(@scope).returns(nil)
+ evaled = ast::ResourceParam.new(:param => 'myparam', :value => object).evaluate(@scope)
+ evaled.should be_a(Puppet::Parser::Resource::Param)
+ evaled.value.should == :undef
+ end
+end
diff --git a/spec/unit/parser/compiler_spec.rb b/spec/unit/parser/compiler_spec.rb
index 8e71756e1..68210a4df 100755
--- a/spec/unit/parser/compiler_spec.rb
+++ b/spec/unit/parser/compiler_spec.rb
@@ -1,5 +1,6 @@
#! /usr/bin/env ruby
require 'spec_helper'
+require 'puppet_spec/compiler'
class CompilerTestResource
attr_accessor :builtin, :virtual, :evaluated, :type, :title
@@ -106,22 +107,20 @@ describe Puppet::Parser::Compiler do
@compiler.classlist.sort.should == %w{one two}.sort
end
- it "should clear the thread local caches before compile" do
+ it "should clear the global caches before compile" do
compiler = stub 'compiler'
Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler
catalog = stub 'catalog'
compiler.expects(:compile).returns catalog
catalog.expects(:to_resource)
- [:known_resource_types, :env_module_directories].each do |var|
- Thread.current[var] = "rspec"
- end
+ $known_resource_types = "rspec"
+ $env_module_directories = "rspec"
Puppet::Parser::Compiler.compile(@node)
- [:known_resource_types, :env_module_directories].each do |var|
- Thread.current[var].should == nil
- end
+ $known_resource_types = nil
+ $env_module_directories = nil
end
describe "when initializing" do
@@ -218,27 +217,6 @@ describe Puppet::Parser::Compiler do
@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)
- compile_stub(:evaluate_node_classes)
-
- @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope)
- @compiler.compile
- end
-
- it "should evaluate any parameterized classes named in the node" do
- classes = {'foo'=>{'p1'=>'one'}, 'bar'=>{'p2'=>'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, "")
@@ -262,12 +240,6 @@ describe Puppet::Parser::Compiler do
@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.
@@ -655,7 +627,7 @@ describe Puppet::Parser::Compiler do
catalog = @compiler.compile
r2 = catalog.resources.detect {|r| r.title == 'Bar::Foo' }
- r2.tags.should =~ ['bar::foo', 'class', 'bar', 'foo']
+ r2.tags.should == Puppet::Util::TagSet.new(['bar::foo', 'class', 'bar', 'foo'])
end
end
@@ -791,6 +763,102 @@ describe Puppet::Parser::Compiler do
end
end
+ describe "when evaluating node classes" do
+ include PuppetSpec::Compiler
+
+ describe "when provided classes in array format" do
+ let(:node) { Puppet::Node.new('someone', :classes => ['something']) }
+
+ describe "when the class exists" do
+ it "should succeed if the class is already included" do
+ manifest = <<-MANIFEST
+ class something {}
+ include something
+ MANIFEST
+
+ catalog = compile_to_catalog(manifest, node)
+
+ catalog.resource('Class', 'Something').should_not be_nil
+ end
+
+ it "should evaluate the class without parameters if it's not already included" do
+ manifest = "class something {}"
+
+ catalog = compile_to_catalog(manifest, node)
+
+ catalog.resource('Class', 'Something').should_not be_nil
+ end
+ end
+
+ it "should fail if the class doesn't exist" do
+ expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/)
+ end
+ end
+
+ describe "when provided classes in hash format" do
+ describe "for classes without parameters" do
+ let(:node) { Puppet::Node.new('someone', :classes => {'something' => {}}) }
+
+ describe "when the class exists" do
+ it "should succeed if the class is already included" do
+ manifest = <<-MANIFEST
+ class something {}
+ include something
+ MANIFEST
+
+ catalog = compile_to_catalog(manifest, node)
+
+ catalog.resource('Class', 'Something').should_not be_nil
+ end
+
+ it "should evaluate the class if it's not already included" do
+ manifest = <<-MANIFEST
+ class something {}
+ MANIFEST
+
+ catalog = compile_to_catalog(manifest, node)
+
+ catalog.resource('Class', 'Something').should_not be_nil
+ end
+ end
+
+ it "should fail if the class doesn't exist" do
+ expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/)
+ end
+ end
+
+ describe "for classes with parameters" do
+ let(:node) { Puppet::Node.new('someone', :classes => {'something' => {'configuron' => 'defrabulated'}}) }
+
+ describe "when the class exists" do
+ it "should fail if the class is already included" do
+ manifest = <<-MANIFEST
+ class something($configuron=frabulated) {}
+ include something
+ MANIFEST
+
+ expect { compile_to_catalog(manifest, node) }.to raise_error(Puppet::Error, /Class\[Something\] is already declared/)
+ end
+
+ it "should evaluate the class if it's not already included" do
+ manifest = <<-MANIFEST
+ class something($configuron=frabulated) {}
+ MANIFEST
+
+ catalog = compile_to_catalog(manifest, node)
+
+ resource = catalog.resource('Class', 'Something')
+ resource['configuron'].should == 'defrabulated'
+ end
+ end
+
+ it "should fail if the class doesn't exist" do
+ expect { compile_to_catalog('', node) }.to raise_error(Puppet::Error, /Could not find class something/)
+ end
+ end
+ end
+ end
+
describe "when managing resource overrides" do
before do
diff --git a/spec/unit/parser/eparser_adapter_spec.rb b/spec/unit/parser/eparser_adapter_spec.rb
index 051633434..173cfb783 100644
--- a/spec/unit/parser/eparser_adapter_spec.rb
+++ b/spec/unit/parser/eparser_adapter_spec.rb
@@ -365,42 +365,42 @@ describe Puppet::Parser do
end
context "when parsing method calls" do
it "should parse method call with one param lambda" do
- expect { @parser.parse("$a.foreach {|$a| debug $a }") }.to_not raise_error
+ expect { @parser.parse("$a.each |$a|{ debug $a }") }.to_not raise_error
end
it "should parse method call with two param lambda" do
- expect { @parser.parse("$a.foreach {|$a,$b| debug $a }") }.to_not raise_error
+ expect { @parser.parse("$a.each |$a,$b|{ debug $a }") }.to_not raise_error
end
it "should parse method call with two param lambda and default value" do
- expect { @parser.parse("$a.foreach {|$a,$b=1| debug $a }") }.to_not raise_error
+ expect { @parser.parse("$a.each |$a,$b=1|{ debug $a }") }.to_not raise_error
end
it "should parse method call without lambda (statement)" do
- expect { @parser.parse("$a.foreach") }.to_not raise_error
+ expect { @parser.parse("$a.each") }.to_not raise_error
end
it "should parse method call without lambda (expression)" do
- expect { @parser.parse("$x = $a.foreach + 1") }.to_not raise_error
+ expect { @parser.parse("$x = $a.each + 1") }.to_not raise_error
end
context "a receiver expression of type" do
it "variable should be allowed" do
- expect { @parser.parse("$a.foreach") }.to_not raise_error
+ expect { @parser.parse("$a.each") }.to_not raise_error
end
it "hasharrayaccess should be allowed" do
- expect { @parser.parse("$a[0][1].foreach") }.to_not raise_error
+ expect { @parser.parse("$a[0][1].each") }.to_not raise_error
end
it "quoted text should be allowed" do
- expect { @parser.parse("\"monkey\".foreach") }.to_not raise_error
- expect { @parser.parse("'monkey'.foreach") }.to_not raise_error
+ expect { @parser.parse("\"monkey\".each") }.to_not raise_error
+ expect { @parser.parse("'monkey'.each") }.to_not raise_error
end
it "selector text should be allowed" do
- expect { @parser.parse("$a ? { 'banana'=>[1,2,3]}.foreach") }.to_not raise_error
+ expect { @parser.parse("$a ? { 'banana'=>[1,2,3]}.each") }.to_not raise_error
end
it "function call should be allowed" do
- expect { @parser.parse("duh(1,2,3).foreach") }.to_not raise_error
+ expect { @parser.parse("duh(1,2,3).each") }.to_not raise_error
end
it "method call should be allowed" do
expect { @parser.parse("$a.foo.bar") }.to_not raise_error
end
it "chained method calls with lambda should be allowed" do
- expect { @parser.parse("$a.foo{||}.bar{||}") }.to_not raise_error
+ expect { @parser.parse("$a.foo||{}.bar||{}") }.to_not raise_error
end
end
end
diff --git a/spec/unit/parser/files_spec.rb b/spec/unit/parser/files_spec.rb
index 2e84216cb..ca7e45b13 100755
--- a/spec/unit/parser/files_spec.rb
+++ b/spec/unit/parser/files_spec.rb
@@ -28,7 +28,7 @@ describe Puppet::Parser::Files do
Puppet[:templatedir] = "/my/templates"
Puppet[:modulepath] = "/one:/two"
File.stubs(:directory?).returns(true)
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
Puppet::Parser::Files.find_template("mymod/mytemplate").should == File.join(Puppet[:templatedir], "mymod/mytemplate")
end
@@ -43,59 +43,59 @@ describe Puppet::Parser::Files do
end
it "should return unqualified templates if they exist in the template dir" do
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.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::FileSystem::File.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::FileSystem::File.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::FileSystem::File.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::FileSystem::File.stubs(:exist?).returns true
Puppet[:templatedir] = "my/templates"
File.expects(:directory?).with(File.expand_path("my/templates")).returns(true)
Puppet::Parser::Files.find_template("mytemplate").should == File.expand_path("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::FileSystem::File.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::FileSystem::File.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::FileSystem::File.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::FileSystem::File.expects(:exist?).with("/one/templates/mytemplate").returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with("/two/templates/mytemplate").returns(true)
Puppet::Parser::Files.find_template("mytemplate").should == "/two/templates/mytemplate"
end
diff --git a/spec/unit/parser/functions/contain_spec.rb b/spec/unit/parser/functions/contain_spec.rb
new file mode 100644
index 000000000..3150e0c8e
--- /dev/null
+++ b/spec/unit/parser/functions/contain_spec.rb
@@ -0,0 +1,185 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet_spec/compiler'
+require 'puppet/parser/functions'
+require 'matchers/containment_matchers'
+require 'matchers/include_in_order'
+
+describe 'The "contain" function' do
+ include PuppetSpec::Compiler
+ include ContainmentMatchers
+
+ it "includes the class" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain contained
+ }
+
+ include container
+ MANIFEST
+
+ expect(catalog.classes).to include("contained")
+ end
+
+ it "makes the class contained in the current class" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain contained
+ }
+
+ include container
+ MANIFEST
+
+ expect(catalog).to contain_class("contained").in("container")
+ end
+
+ it "can contain multiple classes" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class a {
+ notify { "a": }
+ }
+
+ class b {
+ notify { "b": }
+ }
+
+ class container {
+ contain a, b
+ }
+
+ include container
+ MANIFEST
+
+ expect(catalog).to contain_class("a").in("container")
+ expect(catalog).to contain_class("b").in("container")
+ end
+
+ context "when containing a class in multiple classes" do
+ it "creates a catalog with all containment edges" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain contained
+ }
+
+ class another {
+ contain contained
+ }
+
+ include container
+ include another
+ MANIFEST
+
+ expect(catalog).to contain_class("contained").in("container")
+ expect(catalog).to contain_class("contained").in("another")
+ end
+
+ it "and there are no dependencies applies successfully" do
+ manifest = <<-MANIFEST
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain contained
+ }
+
+ class another {
+ contain contained
+ }
+
+ include container
+ include another
+ MANIFEST
+
+ expect { apply_compiled_manifest(manifest) }.not_to raise_error
+ end
+
+ it "and there are explicit dependencies on the containing class causes a dependency cycle" do
+ manifest = <<-MANIFEST
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain contained
+ }
+
+ class another {
+ contain contained
+ }
+
+ include container
+ include another
+
+ Class["container"] -> Class["another"]
+ MANIFEST
+
+ expect { apply_compiled_manifest(manifest) }.to raise_error(
+ Puppet::Error,
+ /Found 1 dependency cycle/
+ )
+ end
+ end
+
+ it "does not create duplicate edges" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain contained
+ contain contained
+ }
+
+ include container
+ MANIFEST
+
+ contained = catalog.resource("Class", "contained")
+ container = catalog.resource("Class", "container")
+
+ expect(catalog.edges_between(container, contained)).to have(1).item
+ end
+
+ context "when a containing class has a dependency order" do
+ it "the contained class is applied in that order" do
+ catalog = compile_to_relationship_graph(<<-MANIFEST)
+ class contained {
+ notify { "contained": }
+ }
+
+ class container {
+ contain contained
+ }
+
+ class first {
+ notify { "first": }
+ }
+
+ class last {
+ notify { "last": }
+ }
+
+ include container, first, last
+
+ Class["first"] -> Class["container"] -> Class["last"]
+ MANIFEST
+
+ expect(order_resources_traversed_in(catalog)).to include_in_order(
+ "Notify[first]", "Notify[contained]", "Notify[last]"
+ )
+ end
+ end
+end
diff --git a/spec/unit/parser/functions/create_resources_spec.rb b/spec/unit/parser/functions/create_resources_spec.rb
index a0c3253ab..79ed02f22 100755
--- a/spec/unit/parser/functions/create_resources_spec.rb
+++ b/spec/unit/parser/functions/create_resources_spec.rb
@@ -23,6 +23,14 @@ describe 'function for dynamically creating resources' do
expect { @scope.function_create_resources(['foo', 'bar', 'blah', 'baz']) }.to raise_error(ArgumentError, 'create_resources(): wrong number of arguments (4; must be 2 or 3)')
end
+ it 'should require second argument to be a hash' do
+ expect { @scope.function_create_resources(['foo','bar']) }.to raise_error(ArgumentError, 'create_resources(): second argument must be a hash')
+ end
+
+ it 'should require optional third argument to be a hash' do
+ expect { @scope.function_create_resources(['foo',{},'foo']) }.to raise_error(ArgumentError, 'create_resources(): third argument, if provided, must be a hash')
+ end
+
describe 'when creating native types' do
it 'empty hash should not cause resources to be added' do
noop_catalog = compile_to_catalog("create_resources('file', {})")
@@ -75,12 +83,12 @@ describe 'function for dynamically creating resources' do
end
describe 'when dynamically creating resource types' do
- it 'should be able to create defined resoure types' do
+ it 'should be able to create defined resource types' do
catalog = compile_to_catalog(<<-MANIFEST)
define foocreateresource($one) {
notify { $name: message => $one }
}
-
+
create_resources('foocreateresource', {'blah'=>{'one'=>'two'}})
MANIFEST
catalog.resource(:notify, "blah")['message'].should == 'two'
@@ -92,7 +100,7 @@ describe 'function for dynamically creating resources' do
define foocreateresource($one) {
notify { $name: message => $one }
}
-
+
create_resources('foocreateresource', {'blah'=>{}})
MANIFEST
}.to raise_error(Puppet::Error, 'Must pass one to Foocreateresource[blah] on node foonode')
@@ -103,7 +111,7 @@ describe 'function for dynamically creating resources' do
define foocreateresource($one) {
notify { $name: message => $one }
}
-
+
create_resources('foocreateresource', {'blah'=>{'one'=>'two'}, 'blaz'=>{'one'=>'three'}})
MANIFEST
@@ -118,7 +126,7 @@ describe 'function for dynamically creating resources' do
}
notify { test: }
-
+
create_resources('foocreateresource', {'blah'=>{'one'=>'two', 'require' => 'Notify[test]'}})
MANIFEST
diff --git a/spec/unit/parser/functions/generate_spec.rb b/spec/unit/parser/functions/generate_spec.rb
index 90afbc8ea..593703d63 100755
--- a/spec/unit/parser/functions/generate_spec.rb
+++ b/spec/unit/parser/functions/generate_spec.rb
@@ -45,7 +45,7 @@ describe "the generate function" do
scope.function_generate([command]).should == 'yay'
end
- describe "on Windows", :as_platform => :windows do
+ describe "on Windows", :if => Puppet.features.microsoft_windows? do
it "should accept the tilde in the path" do
command = "C:/DOCUME~1/ADMINI~1/foo.bat"
Dir.expects(:chdir).with(File.dirname(command)).returns("yay")
diff --git a/spec/unit/parser/functions_spec.rb b/spec/unit/parser/functions_spec.rb
index 81ff655a3..8ad33c874 100755
--- a/spec/unit/parser/functions_spec.rb
+++ b/spec/unit/parser/functions_spec.rb
@@ -128,7 +128,7 @@ describe Puppet::Parser::Functions do
describe "::get_function" do
it "can retrieve a function defined on the *root* environment" do
- Thread.current[:environment] = nil
+ $environment = nil
function = Puppet::Parser::Functions.newfunction("atest", :type => :rvalue) do
nil
end
@@ -162,7 +162,7 @@ describe Puppet::Parser::Functions do
describe "::merged_functions" do
it "returns functions in both the current and root environment" do
- Thread.current[:environment] = nil
+ $environment = nil
func_a = Puppet::Parser::Functions.newfunction("test_a", :type => :rvalue) do
nil
end
diff --git a/spec/unit/parser/lexer_spec.rb b/spec/unit/parser/lexer_spec.rb
index fc8394cb1..972a8f1bf 100755
--- a/spec/unit/parser/lexer_spec.rb
+++ b/spec/unit/parser/lexer_spec.rb
@@ -861,7 +861,7 @@ describe "when trying to lex a non-existent file" do
it "should return an empty list of tokens" do
lexer = Puppet::Parser::Lexer.new
lexer.file = nofile = tmpfile('lexer')
- File.exists?(nofile).should == false
+ Puppet::FileSystem::File.exist?(nofile).should == false
lexer.fullscan.should == [[false,false]]
end
diff --git a/spec/unit/parser/methods/collect_spec.rb b/spec/unit/parser/methods/collect_spec.rb
deleted file mode 100644
index acc26652a..000000000
--- a/spec/unit/parser/methods/collect_spec.rb
+++ /dev/null
@@ -1,153 +0,0 @@
-require 'puppet'
-require 'spec_helper'
-require 'puppet_spec/compiler'
-
-require 'unit/parser/methods/shared'
-
-describe 'the collect method' do
- include PuppetSpec::Compiler
-
- before :each do
- Puppet[:parser] = "future"
- end
-
- context "using future parser" do
- context "in Ruby style should be callable as" do
- it 'collect on an array (multiplying each value by 2)' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [1,2,3]
- $a.collect {|$x| $x*2}.foreach {|$v|
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_4")['ensure'].should == 'present'
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
- end
-
- it 'collect on a hash selecting keys' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.collect {|$x| $x[0]}.foreach {|$k|
- file { "/file_$k": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_a")['ensure'].should == 'present'
- catalog.resource(:file, "/file_b")['ensure'].should == 'present'
- catalog.resource(:file, "/file_c")['ensure'].should == 'present'
- end
-
- it 'foreach on a hash selecting value' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.collect {|$x| $x[1]}.foreach {|$k|
- file { "/file_$k": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
- end
-
- context "handles data type corner cases" do
- it "collect gets values that are false" do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [false,false]
- $a.collect |$x| { $x }.each |$i, $v| {
- file { "/file_$i.$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_0.false")['ensure'].should == 'present'
- catalog.resource(:file, "/file_1.false")['ensure'].should == 'present'
- end
-
- it "collect gets values that are nil" do
- Puppet::Parser::Functions.newfunction(:nil_array, :type => :rvalue) do |args|
- [nil]
- end
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = nil_array()
- $a.collect |$x| { $x }.each |$i, $v| {
- file { "/file_$i.$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_0.")['ensure'].should == 'present'
- end
-
- it "collect gets values that are undef" do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [$does_not_exist]
- $a.collect |$x = "something"| { $x }.each |$i, $v| {
- file { "/file_$i.$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_0.")['ensure'].should == 'present'
- end
- end
-
- context "in Java style should be callable as" do
- shared_examples_for 'java style' do
- it 'collect on an array (multiplying each value by 2)' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [1,2,3]
- $a.collect |$x| #{farr}{ $x*2}.foreach |$v| #{farr}{
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_4")['ensure'].should == 'present'
- catalog.resource(:file, "/file_6")['ensure'].should == 'present'
- end
-
- it 'collect on a hash selecting keys' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.collect |$x| #{farr}{ $x[0]}.foreach |$k| #{farr}{
- file { "/file_$k": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_a")['ensure'].should == 'present'
- catalog.resource(:file, "/file_b")['ensure'].should == 'present'
- catalog.resource(:file, "/file_c")['ensure'].should == 'present'
- end
-
- it 'foreach on a hash selecting value' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>1,'b'=>2,'c'=>3}
- $a.collect |$x| #{farr} {$x[1]}.foreach |$k|#{farr}{
- file { "/file_$k": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
- end
-
- describe 'without fat arrow' do
- it_should_behave_like 'java style' do
- let(:farr) { '' }
- end
- end
-
- describe 'with fat arrow' do
- it_should_behave_like 'java style' do
- let(:farr) { '=>' }
- end
- end
- end
- end
-
- it_should_behave_like 'all iterative functions argument checks', 'collect'
- it_should_behave_like 'all iterative functions hash handling', 'collect'
-end
diff --git a/spec/unit/parser/methods/each_spec.rb b/spec/unit/parser/methods/each_spec.rb
index 4d276e95d..5e9ce4e0c 100644
--- a/spec/unit/parser/methods/each_spec.rb
+++ b/spec/unit/parser/methods/each_spec.rb
@@ -26,7 +26,7 @@ describe 'the each method' do
it 'each on an array selecting each value - function call style' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1,2,3]
- each ($a) |$index, $v| => {
+ each ($a) |$index, $v| {
file { "/file_$v": ensure => present }
}
MANIFEST
diff --git a/spec/unit/parser/methods/select_spec.rb b/spec/unit/parser/methods/filter_spec.rb
index e61ee3a31..89fb15bbb 100644
--- a/spec/unit/parser/methods/select_spec.rb
+++ b/spec/unit/parser/methods/filter_spec.rb
@@ -4,17 +4,17 @@ require 'puppet_spec/compiler'
require 'unit/parser/methods/shared'
-describe 'the select method' do
+describe 'the filter method' do
include PuppetSpec::Compiler
before :each do
Puppet[:parser] = 'future'
end
- it 'should select on an array (all berries)' do
+ it 'should filter on an array (all berries)' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = ['strawberry','blueberry','orange']
- $a.select {|$x| $x =~ /berry$/}.foreach {|$v|
+ $a.filter |$x|{ $x =~ /berry$/}.each |$v|{
file { "/file_$v": ensure => present }
}
MANIFEST
@@ -26,7 +26,7 @@ describe 'the select method' do
it 'should produce an array when acting on an array' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = ['strawberry','blueberry','orange']
- $b = $a.select {|$x| $x =~ /berry$/}
+ $b = $a.filter |$x|{ $x =~ /berry$/}
file { "/file_${b[0]}": ensure => present }
file { "/file_${b[1]}": ensure => present }
MANIFEST
@@ -35,10 +35,10 @@ describe 'the select method' do
catalog.resource(:file, "/file_blueberry")['ensure'].should == 'present'
end
- it 'selects on a hash (all berries) by key' do
+ it 'filters on a hash (all berries) by key' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'}
- $a.select {|$x| $x[0] =~ /berry$/}.foreach {|$v|
+ $a.filter |$x|{ $x[0] =~ /berry$/}.each |$v|{
file { "/file_${v[0]}": ensure => present }
}
MANIFEST
@@ -50,7 +50,7 @@ describe 'the select method' do
it 'should produce a hash when acting on a hash' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'}
- $b = $a.select {|$x| $x[0] =~ /berry$/}
+ $b = $a.filter |$x|{ $x[0] =~ /berry$/}
file { "/file_${b['strawberry']}": ensure => present }
file { "/file_${b['blueberry']}": ensure => present }
file { "/file_${b['orange']}": ensure => present }
@@ -62,10 +62,10 @@ describe 'the select method' do
catalog.resource(:file, "/file_")['ensure'].should == 'present'
end
- it 'selects on a hash (all berries) by value' do
+ it 'filters on a hash (all berries) by value' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {'strawb'=>'red berry','blueb'=>'blue berry','orange'=>'orange fruit'}
- $a.select {|$x| $x[1] =~ /berry$/}.foreach {|$v|
+ $a.filter |$x|{ $x[1] =~ /berry$/}.each |$v|{
file { "/file_${v[0]}": ensure => present }
}
MANIFEST
@@ -74,6 +74,6 @@ describe 'the select method' do
catalog.resource(:file, "/file_blueb")['ensure'].should == 'present'
end
- it_should_behave_like 'all iterative functions argument checks', 'select'
- it_should_behave_like 'all iterative functions hash handling', 'select'
+ it_should_behave_like 'all iterative functions argument checks', 'filter'
+ it_should_behave_like 'all iterative functions hash handling', 'filter'
end
diff --git a/spec/unit/parser/methods/foreach_spec.rb b/spec/unit/parser/methods/foreach_spec.rb
deleted file mode 100755
index 72a9379a4..000000000
--- a/spec/unit/parser/methods/foreach_spec.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-require 'puppet'
-require 'spec_helper'
-require 'puppet_spec/compiler'
-require 'rubygems'
-
-describe 'the foreach method' do
- include PuppetSpec::Compiler
-
- before :each do
- Puppet[:parser] = 'future'
- end
-
- context "should be callable as" do
- it 'foreach on an array selecting each value' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [1,2,3]
- $a.foreach {|$v|
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
- it 'foreach on an array selecting each value - function call style' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [1,2,3]
- foreach ($a) {|$v|
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'present'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
-
- it 'foreach on an array with index' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [present, absent, present]
- $a.foreach {|$k,$v|
- file { "/file_${$k+1}": ensure => $v }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_1")['ensure'].should == 'present'
- catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
-
- it 'foreach on a hash selecting entries' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>'present','b'=>'absent','c'=>'present'}
- $a.foreach {|$e|
- file { "/file_${e[0]}": ensure => $e[1] }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_a")['ensure'].should == 'present'
- catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
- catalog.resource(:file, "/file_c")['ensure'].should == 'present'
- end
- it 'foreach on a hash selecting key and value' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'a'=>present,'b'=>absent,'c'=>present}
- $a.foreach {|$k, $v|
- file { "/file_$k": ensure => $v }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_a")['ensure'].should == 'present'
- catalog.resource(:file, "/file_b")['ensure'].should == 'absent'
- catalog.resource(:file, "/file_c")['ensure'].should == 'present'
- end
- end
- context "should produce receiver" do
- it 'each checking produced value using single expression' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = [1, 3, 2]
- $b = $a.each |$x| { $x }
- file { "/file_${b[1]}":
- ensure => present
- }
- MANIFEST
-
- catalog.resource(:file, "/file_3")['ensure'].should == 'present'
- end
-
- end
-end
diff --git a/spec/unit/parser/methods/map_spec.rb b/spec/unit/parser/methods/map_spec.rb
new file mode 100644
index 000000000..025501754
--- /dev/null
+++ b/spec/unit/parser/methods/map_spec.rb
@@ -0,0 +1,95 @@
+require 'puppet'
+require 'spec_helper'
+require 'puppet_spec/compiler'
+
+require 'unit/parser/methods/shared'
+
+describe 'the map method' do
+ include PuppetSpec::Compiler
+
+ before :each do
+ Puppet[:parser] = "future"
+ end
+
+ context "using future parser" do
+ it 'map on an array (multiplying each value by 2)' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [1,2,3]
+ $a.map |$x|{ $x*2}.each |$v|{
+ file { "/file_$v": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_4")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_6")['ensure'].should == 'present'
+ end
+
+ it 'map on a hash selecting keys' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$x|{ $x[0]}.each |$k|{
+ file { "/file_$k": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_a")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_b")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_c")['ensure'].should == 'present'
+ end
+
+ it 'each on a hash selecting value' do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = {'a'=>1,'b'=>2,'c'=>3}
+ $a.map |$x|{ $x[1]}.each |$k|{
+ file { "/file_$k": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_1")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_2")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_3")['ensure'].should == 'present'
+ end
+
+ context "handles data type corner cases" do
+ it "map gets values that are false" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [false,false]
+ $a.map |$x| { $x }.each |$i, $v| {
+ file { "/file_$i.$v": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_0.false")['ensure'].should == 'present'
+ catalog.resource(:file, "/file_1.false")['ensure'].should == 'present'
+ end
+
+ it "map gets values that are nil" do
+ Puppet::Parser::Functions.newfunction(:nil_array, :type => :rvalue) do |args|
+ [nil]
+ end
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = nil_array()
+ $a.map |$x| { $x }.each |$i, $v| {
+ file { "/file_$i.$v": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_0.")['ensure'].should == 'present'
+ end
+
+ it "map gets values that are undef" do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ $a = [$does_not_exist]
+ $a.map |$x = "something"| { $x }.each |$i, $v| {
+ file { "/file_$i.$v": ensure => present }
+ }
+ MANIFEST
+
+ catalog.resource(:file, "/file_0.")['ensure'].should == 'present'
+ end
+ end
+ it_should_behave_like 'all iterative functions argument checks', 'map'
+ it_should_behave_like 'all iterative functions hash handling', 'map'
+ end
+end
diff --git a/spec/unit/parser/methods/reduce_spec.rb b/spec/unit/parser/methods/reduce_spec.rb
index 99ecfd18d..5d4549b54 100644
--- a/spec/unit/parser/methods/reduce_spec.rb
+++ b/spec/unit/parser/methods/reduce_spec.rb
@@ -28,40 +28,41 @@ describe 'the reduce method' do
it 'reduce on an array' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1,2,3]
- $b = $a.reduce {|$memo, $x| $memo + $x }
+ $b = $a.reduce |$memo, $x| { $memo + $x }
file { "/file_$b": ensure => present }
MANIFEST
catalog.resource(:file, "/file_6")['ensure'].should == 'present'
- end
+ end
+
it 'reduce on an array with start value' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1,2,3]
- $b = $a.reduce(4) {|$memo, $x| $memo + $x }
+ $b = $a.reduce(4) |$memo, $x| { $memo + $x }
file { "/file_$b": ensure => present }
MANIFEST
-
+
catalog.resource(:file, "/file_10")['ensure'].should == 'present'
- end
+ end
it 'reduce on a hash' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {a=>1, b=>2, c=>3}
$start = [ignored, 4]
- $b = $a.reduce {|$memo, $x| ['sum', $memo[1] + $x[1]] }
+ $b = $a.reduce |$memo, $x| {['sum', $memo[1] + $x[1]] }
file { "/file_${$b[0]}_${$b[1]}": ensure => present }
MANIFEST
-
+
catalog.resource(:file, "/file_sum_6")['ensure'].should == 'present'
- end
+ end
it 'reduce on a hash with start value' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {a=>1, b=>2, c=>3}
$start = ['ignored', 4]
- $b = $a.reduce($start) {|$memo, $x| ['sum', $memo[1] + $x[1]] }
+ $b = $a.reduce($start) |$memo, $x| { ['sum', $memo[1] + $x[1]] }
file { "/file_${$b[0]}_${$b[1]}": ensure => present }
MANIFEST
-
+
catalog.resource(:file, "/file_sum_10")['ensure'].should == 'present'
- end
+ end
end
end
diff --git a/spec/unit/parser/methods/reject_spec.rb b/spec/unit/parser/methods/reject_spec.rb
deleted file mode 100644
index e37eaebc5..000000000
--- a/spec/unit/parser/methods/reject_spec.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-require 'puppet'
-require 'spec_helper'
-require 'puppet_spec/compiler'
-
-require 'unit/parser/methods/shared'
-
-describe 'the reject method' do
- include PuppetSpec::Compiler
-
- before :each do
- Puppet[:parser] = 'future'
- end
-
- it 'rejects on an array (no berries)' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = ['strawberry','blueberry','orange']
- $a.reject {|$x| $x =~ /berry$/}.foreach {|$v|
- file { "/file_$v": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_orange")['ensure'].should == 'present'
- catalog.resource(:file, "/file_strawberry").should == nil
- end
-
- it 'produces an array when acting on an array' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = ['strawberry','blueberry','orange']
- $b = $a.reject {|$x| $x =~ /berry$/}
- file { "/file_${b[0]}": ensure => present }
-
- MANIFEST
-
- catalog.resource(:file, "/file_orange")['ensure'].should == 'present'
- catalog.resource(:file, "/file_strawberry").should == nil
- end
-
- it 'rejects on a hash (all berries) by key' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'strawberry'=>'red','blueberry'=>'blue','orange'=>'orange'}
- $a.reject {|$x| $x[0] =~ /berry$/}.foreach {|$v|
- file { "/file_${v[0]}": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_orange")['ensure'].should == 'present'
- end
-
- it 'produces a hash when acting on a hash' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'strawberry'=>'red','blueberry'=>'blue','grape'=>'purple'}
- $b = $a.reject {|$x| $x[0] =~ /berry$/}
- file { "/file_${b[grape]}": ensure => present }
-
- MANIFEST
-
- catalog.resource(:file, "/file_purple")['ensure'].should == 'present'
- end
-
- it 'rejects on a hash (all berries) by value' do
- catalog = compile_to_catalog(<<-MANIFEST)
- $a = {'strawb'=>'red berry','blueb'=>'blue berry','orange'=>'orange fruit'}
- $a.reject {|$x| $x[1] =~ /berry$/}.foreach {|$v|
- file { "/file_${v[0]}": ensure => present }
- }
- MANIFEST
-
- catalog.resource(:file, "/file_orange")['ensure'].should == 'present'
- end
-
- it_should_behave_like 'all iterative functions argument checks', 'reject'
- it_should_behave_like 'all iterative functions hash handling', 'reject'
-end
diff --git a/spec/unit/parser/methods/shared.rb b/spec/unit/parser/methods/shared.rb
index bf9c9f05f..704769d83 100644
--- a/spec/unit/parser/methods/shared.rb
+++ b/spec/unit/parser/methods/shared.rb
@@ -2,7 +2,7 @@
shared_examples_for 'all iterative functions hash handling' do |func|
it 'passes a hash entry as an array of the key and value' do
catalog = compile_to_catalog(<<-MANIFEST)
- {a=>1}.#{func} { |$v| notify { "${v[0]} ${v[1]}": } }
+ {a=>1}.#{func} |$v| { notify { "${v[0]} ${v[1]}": } }
MANIFEST
catalog.resource(:notify, "a 1").should_not be_nil
@@ -14,7 +14,7 @@ shared_examples_for 'all iterative functions argument checks' do |func|
it 'raises an error when defined with more than 1 argument' do
expect do
compile_to_catalog(<<-MANIFEST)
- [1].#{func} { |$x, $yikes| }
+ [1].#{func} |$x, $yikes|{ }
MANIFEST
end.to raise_error(Puppet::Error, /Too few arguments/)
end
@@ -22,7 +22,7 @@ shared_examples_for 'all iterative functions argument checks' do |func|
it 'raises an error when defined with fewer than 1 argument' do
expect do
compile_to_catalog(<<-MANIFEST)
- [1].#{func} { || }
+ [1].#{func} || { }
MANIFEST
end.to raise_error(Puppet::Error, /Too many arguments/)
end
@@ -30,7 +30,7 @@ shared_examples_for 'all iterative functions argument checks' do |func|
it 'raises an error when used against an unsupported type' do
expect do
compile_to_catalog(<<-MANIFEST)
- "not correct".#{func} { |$v| }
+ "not correct".#{func} |$v| { }
MANIFEST
end.to raise_error(Puppet::Error, /must be an Array or a Hash/)
end
@@ -38,7 +38,7 @@ shared_examples_for 'all iterative functions argument checks' do |func|
it 'raises an error when called with any parameters besides a block' do
expect do
compile_to_catalog(<<-MANIFEST)
- [1].#{func}(1) { |$v| }
+ [1].#{func}(1) |$v| { }
MANIFEST
end.to raise_error(Puppet::Error, /Wrong number of arguments/)
end
diff --git a/spec/unit/parser/methods/slice_spec.rb b/spec/unit/parser/methods/slice_spec.rb
index b213415a1..1069bc75a 100644
--- a/spec/unit/parser/methods/slice_spec.rb
+++ b/spec/unit/parser/methods/slice_spec.rb
@@ -27,15 +27,15 @@ describe 'methods' do
end
context "should be callable on array as" do
-
+
it 'slice with explicit parameters' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1, present, 2, absent, 3, present]
- $a.slice(2) |$k,$v| {
+ $a.slice(2) |$k,$v| {
file { "/file_${$k}": ensure => $v }
}
MANIFEST
-
+
catalog.resource(:file, "/file_1")['ensure'].should == 'present'
catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
catalog.resource(:file, "/file_3")['ensure'].should == 'present'
@@ -43,11 +43,11 @@ describe 'methods' do
it 'slice with one parameter' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1, present, 2, absent, 3, present]
- $a.slice(2) |$k| {
+ $a.slice(2) |$k| {
file { "/file_${$k[0]}": ensure => $k[1] }
}
MANIFEST
-
+
catalog.resource(:file, "/file_1")['ensure'].should == 'present'
catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
catalog.resource(:file, "/file_3")['ensure'].should == 'present'
@@ -55,39 +55,39 @@ describe 'methods' do
it 'slice with shorter last slice' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1, present, 2, present, 3, absent]
- $a.slice(4) |$a, $b, $c, $d| {
+ $a.slice(4) |$a, $b, $c, $d| {
file { "/file_$a.$c": ensure => $b }
}
MANIFEST
-
+
catalog.resource(:file, "/file_1.2")['ensure'].should == 'present'
catalog.resource(:file, "/file_3.")['ensure'].should == 'absent'
end
end
context "should be callable on hash as" do
-
+
it 'slice with explicit parameters, missing are empty' do
catalog = compile_to_catalog(<<-MANIFEST)
$a = {1=>present, 2=>present, 3=>absent}
- $a.slice(2) |$a,$b| {
+ $a.slice(2) |$a,$b| {
file { "/file_${a[0]}.${b[0]}": ensure => $a[1] }
}
MANIFEST
-
+
catalog.resource(:file, "/file_1.2")['ensure'].should == 'present'
catalog.resource(:file, "/file_3.")['ensure'].should == 'absent'
end
-
+
end
context "when called without a block" do
it "should produce an array with the result" do
catalog = compile_to_catalog(<<-MANIFEST)
$a = [1, present, 2, absent, 3, present]
- $a.slice(2).each |$k| {
+ $a.slice(2).each |$k| {
file { "/file_${$k[0]}": ensure => $k[1] }
}
MANIFEST
-
+
catalog.resource(:file, "/file_1")['ensure'].should == 'present'
catalog.resource(:file, "/file_2")['ensure'].should == 'absent'
catalog.resource(:file, "/file_3")['ensure'].should == 'present'
diff --git a/spec/unit/parser/parser_spec.rb b/spec/unit/parser/parser_spec.rb
index 38a5c7a85..2c4cf50df 100755
--- a/spec/unit/parser/parser_spec.rb
+++ b/spec/unit/parser/parser_spec.rb
@@ -53,7 +53,7 @@ describe Puppet::Parser do
describe "when parsing files" do
before do
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
File.stubs(:read).returns ""
@parser.stubs(:watch_file)
end
diff --git a/spec/unit/parser/resource/param_spec.rb b/spec/unit/parser/resource/param_spec.rb
new file mode 100755
index 000000000..7989d060d
--- /dev/null
+++ b/spec/unit/parser/resource/param_spec.rb
@@ -0,0 +1,44 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+describe Puppet::Parser::Resource::Param do
+ it "can be instantiated" do
+ Puppet::Parser::Resource::Param.new(:name => 'myparam', :value => 'foo')
+ end
+
+ it "stores the source file" do
+ param = Puppet::Parser::Resource::Param.new(:name => 'myparam', :value => 'foo', :file => 'foo.pp')
+ param.file.should == 'foo.pp'
+ end
+
+ it "stores the line number" do
+ param = Puppet::Parser::Resource::Param.new(:name => 'myparam', :value => 'foo', :line => 42)
+ param.line.should == 42
+ end
+
+ context "parameter validation" do
+ it "throws an error when instantiated without a name" do
+ expect {
+ Puppet::Parser::Resource::Param.new(:value => 'foo')
+ }.to raise_error(Puppet::Error, /name is a required option/)
+ end
+
+ it "throws an error when instantiated without a value" do
+ expect {
+ Puppet::Parser::Resource::Param.new(:name => 'myparam')
+ }.to raise_error(Puppet::Error, /value is a required option/)
+ end
+
+ it "throws an error when instantiated with a nil value" do
+ expect {
+ Puppet::Parser::Resource::Param.new(:name => 'myparam', :value => nil)
+ }.to raise_error(Puppet::Error, /value is a required option/)
+ end
+
+ it "includes file/line context in errors" do
+ expect {
+ Puppet::Parser::Resource::Param.new(:name => 'myparam', :value => nil, :file => 'foo.pp', :line => 42)
+ }.to raise_error(Puppet::Error, /foo.pp:42/)
+ end
+ end
+end
diff --git a/spec/unit/parser/resource_spec.rb b/spec/unit/parser/resource_spec.rb
index 221efb83b..74a66d1c1 100755
--- a/spec/unit/parser/resource_spec.rb
+++ b/spec/unit/parser/resource_spec.rb
@@ -90,7 +90,7 @@ describe Puppet::Parser::Resource do
it "should use a Puppet::Resource for converting to a ral resource" do
trans = mock 'resource', :to_ral => "yay"
@resource = mkresource
- @resource.expects(:to_resource).returns trans
+ @resource.expects(:copy_as_resource).returns trans
@resource.to_ral.should == "yay"
end
@@ -124,7 +124,8 @@ describe Puppet::Parser::Resource do
tags = [ "tag1", "tag2" ]
@arguments[:parameters] = [ param(:tag, tags , :source) ]
res = Puppet::Parser::Resource.new("resource", "testing", @arguments)
- (res.tags & tags).should == tags
+ res.should be_tagged("tag1")
+ res.should be_tagged("tag2")
end
end
@@ -445,7 +446,7 @@ describe Puppet::Parser::Resource do
it "should be able to be converted to a normal resource" do
@source = stub 'scope', :name => "myscope"
@resource = mkresource :source => @source
- @resource.should respond_to(:to_resource)
+ @resource.should respond_to(:copy_as_resource)
end
describe "when being converted to a resource" do
@@ -454,19 +455,19 @@ describe Puppet::Parser::Resource do
end
it "should create an instance of Puppet::Resource" do
- @parser_resource.to_resource.should be_instance_of(Puppet::Resource)
+ @parser_resource.copy_as_resource.should be_instance_of(Puppet::Resource)
end
it "should set the type correctly on the Puppet::Resource" do
- @parser_resource.to_resource.type.should == @parser_resource.type
+ @parser_resource.copy_as_resource.type.should == @parser_resource.type
end
it "should set the title correctly on the Puppet::Resource" do
- @parser_resource.to_resource.title.should == @parser_resource.title
+ @parser_resource.copy_as_resource.title.should == @parser_resource.title
end
it "should copy over all of the parameters" do
- result = @parser_resource.to_resource.to_hash
+ result = @parser_resource.copy_as_resource.to_hash
# The name will be in here, also.
result[:foo].should == "bar"
@@ -477,40 +478,40 @@ describe Puppet::Parser::Resource do
@parser_resource.tag "foo"
@parser_resource.tag "bar"
- @parser_resource.to_resource.tags.should == @parser_resource.tags
+ @parser_resource.copy_as_resource.tags.should == @parser_resource.tags
end
it "should copy over the line" do
@parser_resource.line = 40
- @parser_resource.to_resource.line.should == 40
+ @parser_resource.copy_as_resource.line.should == 40
end
it "should copy over the file" do
@parser_resource.file = "/my/file"
- @parser_resource.to_resource.file.should == "/my/file"
+ @parser_resource.copy_as_resource.file.should == "/my/file"
end
it "should copy over the 'exported' value" do
@parser_resource.exported = true
- @parser_resource.to_resource.exported.should be_true
+ @parser_resource.copy_as_resource.exported.should be_true
end
it "should copy over the 'virtual' value" do
@parser_resource.virtual = true
- @parser_resource.to_resource.virtual.should be_true
+ @parser_resource.copy_as_resource.virtual.should be_true
end
it "should convert any parser resource references to Puppet::Resource instances" do
ref = Puppet::Resource.new("file", "/my/file")
@parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ref}
- result = @parser_resource.to_resource
+ result = @parser_resource.copy_as_resource
result[:fee].should == Puppet::Resource.new(:file, "/my/file")
end
it "should convert any parser resource references to Puppet::Resource instances even if they are in an array" do
ref = Puppet::Resource.new("file", "/my/file")
@parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", ref]}
- result = @parser_resource.to_resource
+ result = @parser_resource.copy_as_resource
result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file")]
end
@@ -518,7 +519,7 @@ describe Puppet::Parser::Resource do
ref1 = Puppet::Resource.new("file", "/my/file1")
ref2 = Puppet::Resource.new("file", "/my/file2")
@parser_resource = mkresource :source => @source, :parameters => {:foo => "bar", :fee => ["a", [ref1,ref2]]}
- result = @parser_resource.to_resource
+ result = @parser_resource.copy_as_resource
result[:fee].should == ["a", Puppet::Resource.new(:file, "/my/file1"), Puppet::Resource.new(:file, "/my/file2")]
end
diff --git a/spec/unit/pops/model/ast_transformer_spec.rb b/spec/unit/pops/model/ast_transformer_spec.rb
index f2b1c9b56..969e944de 100644
--- a/spec/unit/pops/model/ast_transformer_spec.rb
+++ b/spec/unit/pops/model/ast_transformer_spec.rb
@@ -13,28 +13,42 @@ describe Puppet::Pops::Model::AstTransformer do
it "converts a decimal number to a string Name" do
ast = transform(QNAME_OR_NUMBER("10"))
- ast.should be_kind_of Puppet::Parser::AST::Name
+ ast.should be_kind_of(Puppet::Parser::AST::Name)
ast.value.should == "10"
end
+ it "converts a 0 to a decimal 0" do
+ ast = transform(QNAME_OR_NUMBER("0"))
+
+ ast.should be_kind_of(Puppet::Parser::AST::Name)
+ ast.value.should == "0"
+ end
+
+ it "converts a 00 to an octal 00" do
+ ast = transform(QNAME_OR_NUMBER("0"))
+
+ ast.should be_kind_of(Puppet::Parser::AST::Name)
+ ast.value.should == "0"
+ end
+
it "converts an octal number to a string Name" do
ast = transform(QNAME_OR_NUMBER("020"))
- ast.should be_kind_of Puppet::Parser::AST::Name
+ ast.should be_kind_of(Puppet::Parser::AST::Name)
ast.value.should == "020"
end
it "converts a hex number to a string Name" do
ast = transform(QNAME_OR_NUMBER("0x20"))
- ast.should be_kind_of Puppet::Parser::AST::Name
+ ast.should be_kind_of(Puppet::Parser::AST::Name)
ast.value.should == "0x20"
end
it "converts an unknown radix to an error string" do
ast = transform(Puppet::Pops::Model::Factory.new(Puppet::Pops::Model::LiteralNumber, 3, 2))
- ast.should be_kind_of Puppet::Parser::AST::Name
+ ast.should be_kind_of(Puppet::Parser::AST::Name)
ast.value.should == "bad radix:3"
end
end
diff --git a/spec/unit/pops/parser/lexer_spec.rb b/spec/unit/pops/parser/lexer_spec.rb
index 985807261..71010c033 100755
--- a/spec/unit/pops/parser/lexer_spec.rb
+++ b/spec/unit/pops/parser/lexer_spec.rb
@@ -154,7 +154,7 @@ describe Puppet::Pops::Parser::Lexer::TOKENS do
:LBRACK => '[',
:RBRACK => ']',
# :LBRACE => '{',
- :RBRACE => '}',
+# :RBRACE => '}',
:LPAREN => '(',
:RPAREN => ')',
:EQUALS => '=',
@@ -232,7 +232,7 @@ describe Puppet::Pops::Parser::Lexer::TOKENS do
# These tokens' strings don't matter, just that the tokens exist.
[:STRING, :DQPRE, :DQMID, :DQPOST, :BOOLEAN, :NAME, :NUMBER, :COMMENT, :MLCOMMENT,
- :LBRACE, :LAMBDA,
+ :LBRACE, :RBRACE,
:RETURN, :SQUOTE, :DQUOTE, :VARIABLE].each do |name|
it "should have a token named #{name.to_s}" do
Puppet::Pops::Parser::Lexer::TOKENS[name].should_not be_nil
@@ -583,7 +583,24 @@ describe Puppet::Pops::Parser::Lexer,"when lexing strings" do
%q[""] => [[:STRING,""]],
%q["123 456 789 0"] => [[:STRING,"123 456 789 0"]],
%q["${123} 456 $0"] => [[:DQPRE,""],[:VARIABLE,"123"],[:DQMID," 456 "],[:VARIABLE,"0"],[:DQPOST,""]],
- %q["$foo::::bar"] => [[:DQPRE,""],[:VARIABLE,"foo"],[:DQPOST,"::::bar"]]
+ %q["$foo::::bar"] => [[:DQPRE,""],[:VARIABLE,"foo"],[:DQPOST,"::::bar"]],
+ # Keyword variables
+ %q["$true"] => [[:DQPRE,""],[:VARIABLE, "true"],[:DQPOST,""]],
+ %q["$false"] => [[:DQPRE,""],[:VARIABLE, "false"],[:DQPOST,""]],
+ %q["$if"] => [[:DQPRE,""],[:VARIABLE, "if"],[:DQPOST,""]],
+ %q["$case"] => [[:DQPRE,""],[:VARIABLE, "case"],[:DQPOST,""]],
+ %q["$unless"] => [[:DQPRE,""],[:VARIABLE, "unless"],[:DQPOST,""]],
+ %q["$undef"] => [[:DQPRE,""],[:VARIABLE, "undef"],[:DQPOST,""]],
+ # Expressions
+ %q["${true}"] => [[:DQPRE,""],[:BOOLEAN, true],[:DQPOST,""]],
+ %q["${false}"] => [[:DQPRE,""],[:BOOLEAN, false],[:DQPOST,""]],
+ %q["${undef}"] => [[:DQPRE,""],:UNDEF,[:DQPOST,""]],
+ %q["${if true {false}}"] => [[:DQPRE,""],:IF,[:BOOLEAN, true], :LBRACE, [:BOOLEAN, false], :RBRACE, [:DQPOST,""]],
+ %q["${unless true {false}}"] => [[:DQPRE,""],:UNLESS,[:BOOLEAN, true], :LBRACE, [:BOOLEAN, false], :RBRACE, [:DQPOST,""]],
+ %q["${case true {true:{false}}}"] => [
+ [:DQPRE,""],:CASE,[:BOOLEAN, true], :LBRACE, [:BOOLEAN, true], :COLON, :LBRACE, [:BOOLEAN, false],
+ :RBRACE, :RBRACE, [:DQPOST,""]],
+ %q[{ "${a}" => 1 }] => [ :LBRACE, [:DQPRE,""], [:VARIABLE,"a"], [:DQPOST,""], :FARROW, [:NAME,"1"], :RBRACE ],
}.each { |src,expected_result|
it "should handle #{src} correctly" do
EgrammarLexerSpec.tokens_scanned_from(src).should be_like(*expected_result)
@@ -742,7 +759,7 @@ describe "Puppet::Pops::Parser::Lexer in the old tests" do
it "should end variables at `-`" do
EgrammarLexerSpec.tokens_scanned_from('$hyphenated-variable').
- should be_like [:VARIABLE, "hyphenated"], [:MINUS, '-'], [:NAME, 'variable']
+ should be_like([:VARIABLE, "hyphenated"], [:MINUS, '-'], [:NAME, 'variable'])
end
it "should not include whitespace in a variable" do
@@ -769,7 +786,7 @@ describe "when trying to lex a non-existent file" do
it "should return an empty list of tokens" do
lexer = Puppet::Pops::Parser::Lexer.new
lexer.file = nofile = tmpfile('lexer')
- File.exists?(nofile).should == false
+ Puppet::FileSystem::File.exist?(nofile).should == false
lexer.fullscan.should == [[false,false]]
end
diff --git a/spec/unit/pops/parser/parse_calls_spec.rb b/spec/unit/pops/parser/parse_calls_spec.rb
index 647d0e16c..800a15bec 100644
--- a/spec/unit/pops/parser/parse_calls_spec.rb
+++ b/spec/unit/pops/parser/parse_calls_spec.rb
@@ -81,16 +81,16 @@ describe "egrammar parsing function calls" do
dump(parse("$a.foo")).should == "(call-method (. $a foo))"
end
- it "$a.foo {|| }" do
+ it "$a.foo || { }" do
dump(parse("$a.foo || { }")).should == "(call-method (. $a foo) (lambda ()))"
end
- it "$a.foo {|$x| }" do
- dump(parse("$a.foo {|$x| }")).should == "(call-method (. $a foo) (lambda (parameters x) ()))"
+ it "$a.foo |$x| { }" do
+ dump(parse("$a.foo |$x|{ }")).should == "(call-method (. $a foo) (lambda (parameters x) ()))"
end
- it "$a.foo {|$x| }" do
- dump(parse("$a.foo {|$x| $b = $x}")).should ==
+ it "$a.foo |$x|{ }" do
+ dump(parse("$a.foo |$x|{ $b = $x}")).should ==
"(call-method (. $a foo) (lambda (parameters x) (block (= $b $x))))"
end
end
diff --git a/spec/unit/pops/transformer/transform_calls_spec.rb b/spec/unit/pops/transformer/transform_calls_spec.rb
index f2303db5b..969a2d7b8 100644
--- a/spec/unit/pops/transformer/transform_calls_spec.rb
+++ b/spec/unit/pops/transformer/transform_calls_spec.rb
@@ -60,20 +60,20 @@ describe "transformation to Puppet AST for function calls" do
astdump(parse("$a.foo")).should == "(call-method (. $a foo))"
end
- it "$a.foo {|| }" do
+ it "$a.foo ||{ }" do
astdump(parse("$a.foo || { }")).should == "(call-method (. $a foo) (lambda ()))"
end
- it "$a.foo {|| []} # check transformation to block with empty array" do
- astdump(parse("$a.foo || { []}")).should == "(call-method (. $a foo) (lambda (block ([]))))"
+ it "$a.foo ||{[]} # check transformation to block with empty array" do
+ astdump(parse("$a.foo || {[]}")).should == "(call-method (. $a foo) (lambda (block ([]))))"
end
it "$a.foo {|$x| }" do
- astdump(parse("$a.foo {|$x| }")).should == "(call-method (. $a foo) (lambda (parameters x) ()))"
+ astdump(parse("$a.foo |$x| { }")).should == "(call-method (. $a foo) (lambda (parameters x) ()))"
end
- it "$a.foo {|$x| $b = $x}" do
- astdump(parse("$a.foo {|$x| $b = $x}")).should ==
+ it "$a.foo |$x| { $b = $x}" do
+ astdump(parse("$a.foo |$x| { $b = $x}")).should ==
"(call-method (. $a foo) (lambda (parameters x) (block (= $b $x))))"
end
end
diff --git a/spec/unit/pops/transformer/transform_containers_spec.rb b/spec/unit/pops/transformer/transform_containers_spec.rb
index 682860fe1..8c65e2bcc 100644
--- a/spec/unit/pops/transformer/transform_containers_spec.rb
+++ b/spec/unit/pops/transformer/transform_containers_spec.rb
@@ -154,12 +154,12 @@ describe "transformation to Puppet AST for containers" do
it "node foo inherits 'bar' {}" do
# AST can not differentiate between bare word and string
- astdump(parse("node foo inherits 'bar' {}")).should == "(node (matches 'foo') (parent 'bar') ())"
+ astdump(parse("node foo inherits 'bar' {}")).should == "(node (matches 'foo') (parent bar) ())"
end
it "node foo inherits default {}" do
# AST can not differentiate between bare word and string
- astdump(parse("node foo inherits default {}")).should == "(node (matches 'foo') (parent :default) ())"
+ astdump(parse("node foo inherits default {}")).should == "(node (matches 'foo') (parent default) ())"
end
it "node /web.*/ {}" do
diff --git a/spec/unit/pops/validator/validator_spec.rb b/spec/unit/pops/validator/validator_spec.rb
new file mode 100644
index 000000000..d54d2ca61
--- /dev/null
+++ b/spec/unit/pops/validator/validator_spec.rb
@@ -0,0 +1,31 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/pops'
+require 'puppet_spec/pops'
+
+# relative to this spec file (./) does not work as this file is loaded by rspec
+require File.join(File.dirname(__FILE__), '../parser/parser_rspec_helper')
+
+describe "validating 3x" do
+ include ParserRspecHelper
+ include PuppetSpec::Pops
+
+ let(:acceptor) { Puppet::Pops::Validation::Acceptor.new() }
+ let(:validator) { Puppet::Pops::Validation::ValidatorFactory_3_1.new().validator(acceptor) }
+
+ def validate(model)
+ validator.validate(model)
+ acceptor
+ end
+
+ it 'should raise error for illegal names' do
+ expect(validate(fqn('Aaa'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
+ expect(validate(fqn('AAA'))).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
+ end
+
+ it 'should raise error for illegal variable names' do
+ expect(validate(fqn('Aaa').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
+ expect(validate(fqn('AAA').var())).to have_issue(Puppet::Pops::Issues::ILLEGAL_NAME)
+ end
+
+end \ No newline at end of file
diff --git a/spec/unit/provider/augeas/augeas_spec.rb b/spec/unit/provider/augeas/augeas_spec.rb
index e75a0d36e..9b6e88ce8 100755
--- a/spec/unit/provider/augeas/augeas_spec.rb
+++ b/spec/unit/provider/augeas/augeas_spec.rb
@@ -276,6 +276,61 @@ describe provider_class do
@provider.need_to_run?.should == true
end
+ describe "performing numeric comparisons (#22617)" do
+ it "should return true when a get string compare is true" do
+ @resource[:onlyif] = "get bpath > a"
+ @augeas.stubs("get").returns("b")
+ @provider.need_to_run?.should == true
+ end
+
+ it "should return false when a get string compare is false" do
+ @resource[:onlyif] = "get a19path > a2"
+ @augeas.stubs("get").returns("a19")
+ @provider.need_to_run?.should == false
+ end
+
+ it "should return true when a get int gt compare is true" do
+ @resource[:onlyif] = "get path19 > 2"
+ @augeas.stubs("get").returns("19")
+ @provider.need_to_run?.should == true
+ end
+
+ it "should return true when a get int ge compare is true" do
+ @resource[:onlyif] = "get path19 >= 2"
+ @augeas.stubs("get").returns("19")
+ @provider.need_to_run?.should == true
+ end
+
+ it "should return true when a get int lt compare is true" do
+ @resource[:onlyif] = "get path2 < 19"
+ @augeas.stubs("get").returns("2")
+ @provider.need_to_run?.should == true
+ end
+
+ it "should return false when a get int le compare is false" do
+ @resource[:onlyif] = "get path39 <= 4"
+ @augeas.stubs("get").returns("39")
+ @provider.need_to_run?.should == false
+ end
+ end
+ describe "performing is_numeric checks (#22617)" do
+ it "should return false for nil" do
+ @provider.is_numeric?(nil).should == false
+ end
+ it "should return true for Fixnums" do
+ @provider.is_numeric?(9).should == true
+ end
+ it "should return true for numbers in Strings" do
+ @provider.is_numeric?('9').should == true
+ end
+ it "should return false for non-number Strings" do
+ @provider.is_numeric?('x9').should == false
+ end
+ it "should return false for other types" do
+ @provider.is_numeric?([true]).should == false
+ end
+ end
+
it "should return false when a get filter does not match" do
@resource[:onlyif] = "get path == another value"
@augeas.stubs("get").returns("value")
@@ -619,7 +674,7 @@ describe provider_class do
link = tmpfile('link')
target = tmpfile('target')
FileUtils.touch(target)
- FileUtils.symlink(target, link)
+ Puppet::FileSystem::File.new(target).symlink(link)
resource = Puppet::Type.type(:augeas).new(
:name => 'test',
@@ -634,7 +689,7 @@ describe provider_class do
catalog.apply
File.ftype(link).should == 'link'
- File.readlink(link).should == target
+ Puppet::FileSystem::File.new(link).readlink().should == target
File.read(target).should =~ /PermitRootLogin no/
end
end
diff --git a/spec/unit/provider/exec/posix_spec.rb b/spec/unit/provider/exec/posix_spec.rb
index 39cc10b41..7c4982fcc 100755
--- a/spec/unit/provider/exec/posix_spec.rb
+++ b/spec/unit/provider/exec/posix_spec.rb
@@ -64,7 +64,7 @@ describe Puppet::Type.type(:exec).provider(:posix) do
provider.resource[:path] = [File.dirname(command)]
filename = File.basename(command)
- Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == filename) && (arguments.is_a? Hash) }
+ Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == filename) && (arguments.is_a? Hash) }.returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run(filename)
end
@@ -95,7 +95,7 @@ describe Puppet::Type.type(:exec).provider(:posix) do
provider.resource[:path] = ['/bogus/bin']
command = make_exe
- Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == "#{command} bar --sillyarg=true --blah") && (arguments.is_a? Hash) }
+ Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == "#{command} bar --sillyarg=true --blah") && (arguments.is_a? Hash) }.returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run("#{command} bar --sillyarg=true --blah")
end
@@ -110,11 +110,16 @@ describe Puppet::Type.type(:exec).provider(:posix) do
provider.resource[:environment] = ['WHATEVER=/something/else', 'WHATEVER=/foo']
command = make_exe
- Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == command) && (arguments.is_a? Hash) }
+ Puppet::Util::Execution.expects(:execute).with { |cmdline, arguments| (cmdline == command) && (arguments.is_a? Hash) }.returns(Puppet::Util::Execution::ProcessOutput.new('', 0))
provider.run(command)
@logs.map {|l| "#{l.level}: #{l.message}" }.should == ["warning: Overriding environment setting 'WHATEVER' with '/foo'"]
end
+ it "should set umask before execution if umask parameter is in use" do
+ provider.resource[:umask] = '0027'
+ Puppet::Util.expects(:withumask).with(0027)
+ provider.run(provider.resource[:command])
+ end
describe "posix locale settings", :unless => Puppet.features.microsoft_windows? do
# a sentinel value that we can use to emulate what locale environment variables might be set to on an international
diff --git a/spec/unit/provider/file/posix_spec.rb b/spec/unit/provider/file/posix_spec.rb
index e84d23a5a..48eaae30a 100755
--- a/spec/unit/provider/file/posix_spec.rb
+++ b/spec/unit/provider/file/posix_spec.rb
@@ -85,7 +85,7 @@ describe Puppet::Type.type(:file).provider(:posix), :if => Puppet.features.posix
describe "#owner" do
it "should return the uid of the file owner" do
FileUtils.touch(path)
- owner = File.stat(path).uid
+ owner = Puppet::FileSystem::File.new(path).stat.uid
provider.owner.should == owner
end
@@ -178,7 +178,7 @@ describe Puppet::Type.type(:file).provider(:posix), :if => Puppet.features.posix
describe "#group" do
it "should return the gid of the file group" do
FileUtils.touch(path)
- group = File.stat(path).gid
+ group = Puppet::FileSystem::File.new(path).stat.gid
provider.group.should == group
end
diff --git a/spec/unit/provider/group/windows_adsi_spec.rb b/spec/unit/provider/group/windows_adsi_spec.rb
index 21b967509..d28601172 100644
--- a/spec/unit/provider/group/windows_adsi_spec.rb
+++ b/spec/unit/provider/group/windows_adsi_spec.rb
@@ -24,12 +24,64 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
names = ['group1', 'group2', 'group3']
stub_groups = names.map{|n| stub(:name => n)}
- connection.stubs(:execquery).with("select name from win32_group").returns stub_groups
+ connection.stubs(:execquery).with('select name from win32_group where localaccount = "TRUE"').returns stub_groups
described_class.instances.map(&:name).should =~ names
end
end
+ describe "group type :members property helpers", :if => Puppet.features.microsoft_windows? do
+
+ let(:user1) { stub(:account => 'user1', :domain => '.', :to_s => 'user1sid') }
+ let(:user2) { stub(:account => 'user2', :domain => '.', :to_s => 'user2sid') }
+
+ before :each do
+ Puppet::Util::Windows::Security.stubs(:name_to_sid_object).with('user1').returns(user1)
+ Puppet::Util::Windows::Security.stubs(:name_to_sid_object).with('user2').returns(user2)
+ end
+
+ describe "#members_insync?" do
+ it "should return false when current is nil" do
+ provider.members_insync?(nil, ['user2']).should be_false
+ end
+ it "should return false when should is nil" do
+ provider.members_insync?(['user1'], nil).should be_false
+ end
+ it "should return false for differing lists of members" do
+ provider.members_insync?(['user1'], ['user2']).should be_false
+ provider.members_insync?(['user1'], []).should be_false
+ provider.members_insync?([], ['user2']).should be_false
+ end
+ it "should return true for same lists of members" do
+ provider.members_insync?(['user1', 'user2'], ['user1', 'user2']).should be_true
+ end
+ it "should return true for same lists of unordered members" do
+ provider.members_insync?(['user1', 'user2'], ['user2', 'user1']).should be_true
+ end
+ it "should return true for same lists of members irrespective of duplicates" do
+ provider.members_insync?(['user1', 'user2', 'user2'], ['user2', 'user1', 'user1']).should be_true
+ end
+ end
+
+ describe "#members_to_s" do
+ it "should return an empty string on non-array input" do
+ [Object.new, {}, 1, :symbol, ''].each do |input|
+ provider.members_to_s(input).should be_empty
+ end
+ end
+ it "should return an empty string on empty or nil users" do
+ provider.members_to_s([]).should be_empty
+ provider.members_to_s(nil).should be_empty
+ end
+ it "should return a user string like DOMAIN\\USER" do
+ provider.members_to_s(['user1']).should == '.\user1'
+ end
+ it "should return a user string like DOMAIN\\USER,DOMAIN2\\USER2" do
+ provider.members_to_s(['user1', 'user2']).should == '.\user1,.\user2'
+ end
+ end
+ end
+
describe "when managing members" do
it "should be able to provide a list of members" do
provider.group.stubs(:members).returns ['user1', 'user2', 'user3']
@@ -37,11 +89,22 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
provider.members.should =~ ['user1', 'user2', 'user3']
end
- it "should be able to set group members" do
+ it "should be able to set group members", :if => Puppet.features.microsoft_windows? do
provider.group.stubs(:members).returns ['user1', 'user2']
- provider.group.expects(:remove_members).with('user1')
- provider.group.expects(:add_members).with('user3')
+ member_sids = [
+ stub(:account => 'user1', :domain => 'testcomputername'),
+ stub(:account => 'user2', :domain => 'testcomputername'),
+ stub(:account => 'user3', :domain => 'testcomputername'),
+ ]
+
+ provider.group.stubs(:member_sids).returns(member_sids[0..1])
+
+ Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('user2').returns(member_sids[1])
+ Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('user3').returns(member_sids[2])
+
+ provider.group.expects(:remove_member_sids).with(member_sids[0])
+ provider.group.expects(:add_member_sids).with(member_sids[2])
provider.members = ['user2', 'user3']
end
@@ -97,4 +160,8 @@ describe Puppet::Type.type(:group).provider(:windows_adsi) do
provider.expects(:fail).with { |msg| msg =~ /gid is read-only/ }
provider.send(:gid=, 500)
end
+
+ it "should prefer the domain component from the resolved SID", :if => Puppet.features.microsoft_windows? do
+ provider.members_to_s(['.\Administrators']).should == 'BUILTIN\Administrators'
+ end
end
diff --git a/spec/unit/provider/nameservice/directoryservice_spec.rb b/spec/unit/provider/nameservice/directoryservice_spec.rb
index e7fdcbef5..fbb74d0f3 100755
--- a/spec/unit/provider/nameservice/directoryservice_spec.rb
+++ b/spec/unit/provider/nameservice/directoryservice_spec.rb
@@ -110,7 +110,7 @@ describe 'DirectoryService password behavior' do
it 'should execute convert_binary_to_xml once when getting the password on >= 10.7' do
subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)})
- File.expects(:exists?).with(plist_path).once.returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(plist_path).once.returns(true)
Plist.expects(:parse_xml).returns(shadow_hash_data)
# On Mac OS X 10.7 we first need to convert to xml when reading the password
subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path)
@@ -126,7 +126,7 @@ describe 'DirectoryService password behavior' do
it 'should convert xml-to-binary and binary-to-xml when setting the pw on >= 10.7' do
subject.expects(:convert_binary_to_xml).returns({'SALTED-SHA512' => StringIO.new(pw_string)})
subject.expects(:convert_xml_to_binary).returns(binary_plist)
- File.expects(:exists?).with(plist_path).once.returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(plist_path).once.returns(true)
Plist.expects(:parse_xml).returns(shadow_hash_data)
# On Mac OS X 10.7 we first need to convert to xml
subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path)
@@ -138,7 +138,7 @@ describe 'DirectoryService password behavior' do
it '[#13686] should handle an empty ShadowHashData field in the users plist' do
subject.expects(:convert_xml_to_binary).returns(binary_plist)
- File.expects(:exists?).with(plist_path).once.returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(plist_path).once.returns(true)
Plist.expects(:parse_xml).returns({'ShadowHashData' => nil})
subject.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout', plist_path)
subject.expects(:plutil).with('-convert', 'binary1', plist_path)
diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb
index 5e75a569f..5d7b3e4e6 100755
--- a/spec/unit/provider/package/apt_spec.rb
+++ b/spec/unit/provider/package/apt_spec.rb
@@ -63,7 +63,7 @@ Version table:
it "should preseed with the provided responsefile when preseeding is called for" do
@resource.expects(:[]).with(:responsefile).returns "/my/file"
- FileTest.expects(:exist?).with("/my/file").returns true
+ Puppet::FileSystem::File.expects(:exist?).with("/my/file").returns true
@provider.expects(:info)
@provider.expects(:preseed).with("/my/file")
diff --git a/spec/unit/provider/package/aptrpm_spec.rb b/spec/unit/provider/package/aptrpm_spec.rb
index 6c77bee3a..83d343242 100755
--- a/spec/unit/provider/package/aptrpm_spec.rb
+++ b/spec/unit/provider/package/aptrpm_spec.rb
@@ -19,7 +19,7 @@ describe Puppet::Type.type(:package).provider(:aptrpm) do
def rpm
pkg.provider.expects(:rpm).
with('-q', 'faff', '--nosignature', '--nodigest', '--qf',
- "'%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH} :DESC: %{SUMMARY}\\n'")
+ "%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH} :DESC: %{SUMMARY}\\n")
end
it "should report absent packages" do
diff --git a/spec/unit/provider/package/msi_spec.rb b/spec/unit/provider/package/msi_spec.rb
index 50ff40deb..192b0a749 100755
--- a/spec/unit/provider/package/msi_spec.rb
+++ b/spec/unit/provider/package/msi_spec.rb
@@ -24,9 +24,8 @@ describe Puppet::Type.type(:package).provider(:msi) do
MsiPackage.stubs(:installer).returns(installer)
end
- before :each do
- # make sure we never try to execute msiexec
- provider.expects(:execute).never
+ def expect_execute(command, status)
+ provider.expects(:execute).with(command, execute_options).returns(Puppet::Util::Execution::ProcessOutput.new('',status))
end
describe 'provider features' do
@@ -38,6 +37,7 @@ describe Puppet::Type.type(:package).provider(:msi) do
describe 'on Windows', :as_platform => :windows do
it 'should not be the default provider' do
+ # provider.expects(:execute).never
Puppet::Type.type(:package).defaultprovider.should_not == subject.class
end
end
@@ -90,9 +90,7 @@ describe Puppet::Type.type(:package).provider(:msi) do
end
context '#install' do
- before :each do
- provider.stubs(:execute)
- end
+ let (:command) { "msiexec.exe /qn /norestart /i #{source}" }
it 'should require the source parameter' do
resource = Puppet::Type.type(:package).new(:name => name, :provider => :msi)
@@ -104,40 +102,27 @@ describe Puppet::Type.type(:package).provider(:msi) do
it 'should install using the source and install_options' do
resource[:install_options] = { 'INSTALLDIR' => 'C:\mysql-5.1' }
-
- provider.expects(:execute).with("msiexec.exe /qn /norestart /i #{source} INSTALLDIR=C:\\mysql-5.1", execute_options)
- provider.expects(:exit_status).returns(0)
-
- provider.install
- end
-
- it 'should warn if the package requests a reboot' do
- provider.stubs(:exit_status).returns(194)
-
- provider.expects(:warning).with('The package requested a reboot to finish the operation.')
+ expect_execute("#{command} INSTALLDIR=C:\\mysql-5.1", 0)
provider.install
end
it 'should warn if reboot initiated' do
- provider.stubs(:exit_status).returns(1641)
-
+ expect_execute(command, 1641)
provider.expects(:warning).with('The package installed successfully and the system is rebooting now.')
provider.install
end
it 'should warn if reboot required' do
- provider.stubs(:exit_status).returns(3010)
-
+ expect_execute(command, 3010)
provider.expects(:warning).with('The package installed successfully, but the system must be rebooted.')
provider.install
end
it 'should fail otherwise', :if => Puppet.features.microsoft_windows? do
- provider.stubs(:execute)
- provider.stubs(:exit_status).returns(5)
+ expect_execute(command, 5)
expect do
provider.install
@@ -146,6 +131,9 @@ describe Puppet::Type.type(:package).provider(:msi) do
end
context '#uninstall' do
+
+ let (:command) { "msiexec.exe /qn /norestart /x #{productcode}" }
+
before :each do
resource[:ensure] = :absent
provider.set(:productcode => productcode)
@@ -159,42 +147,27 @@ describe Puppet::Type.type(:package).provider(:msi) do
end
it 'should uninstall using the productcode' do
- provider.expects(:execute).with("msiexec.exe /qn /norestart /x #{productcode}", execute_options)
- provider.expects(:exit_status).returns(0)
-
- provider.uninstall
- end
-
- it 'should warn if the package requests a reboot' do
- provider.stubs(:execute)
- provider.stubs(:exit_status).returns(194)
-
- provider.expects(:warning).with('The package requested a reboot to finish the operation.')
+ expect_execute(command, 0)
provider.uninstall
end
it 'should warn if reboot initiated' do
- provider.stubs(:execute)
- provider.stubs(:exit_status).returns(1641)
-
+ expect_execute(command, 1641)
provider.expects(:warning).with('The package uninstalled successfully and the system is rebooting now.')
provider.uninstall
end
it 'should warn if reboot required' do
- provider.stubs(:execute)
- provider.stubs(:exit_status).returns(3010)
-
+ expect_execute(command, 3010)
provider.expects(:warning).with('The package uninstalled successfully, but the system must be rebooted.')
provider.uninstall
end
it 'should fail otherwise', :if => Puppet.features.microsoft_windows? do
- provider.stubs(:execute)
- provider.stubs(:exit_status).returns(5)
+ expect_execute(command, 5)
expect do
provider.uninstall
diff --git a/spec/unit/provider/package/openbsd_spec.rb b/spec/unit/provider/package/openbsd_spec.rb
index 5a7ef7b46..48ec4bedb 100755
--- a/spec/unit/provider/package/openbsd_spec.rb
+++ b/spec/unit/provider/package/openbsd_spec.rb
@@ -10,7 +10,7 @@ describe provider_class do
def expect_read_from_pkgconf(lines)
pkgconf = stub(:readlines => lines)
- File.expects(:exist?).with('/etc/pkg.conf').returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with('/etc/pkg.conf').returns(true)
File.expects(:open).with('/etc/pkg.conf', 'rb').returns(pkgconf)
end
@@ -76,7 +76,7 @@ describe provider_class do
context "#install" do
it "should fail if the resource doesn't have a source" do
- File.expects(:exist?).with('/etc/pkg.conf').returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with('/etc/pkg.conf').returns(false)
expect {
provider.install
@@ -84,7 +84,7 @@ describe provider_class do
end
it "should fail if /etc/pkg.conf exists, but is not readable" do
- File.expects(:exist?).with('/etc/pkg.conf').returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with('/etc/pkg.conf').returns(true)
File.expects(:open).with('/etc/pkg.conf', 'rb').raises(Errno::EACCES)
expect {
diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb
index e438b51a5..e81abafde 100755
--- a/spec/unit/provider/package/rpm_spec.rb
+++ b/spec/unit/provider/package/rpm_spec.rb
@@ -20,7 +20,8 @@ describe provider_class do
let(:resource) do
Puppet::Type.type(:package).new(
:name => resource_name,
- :ensure => :installed
+ :ensure => :installed,
+ :provider => 'rpm'
)
end
@@ -30,7 +31,7 @@ describe provider_class do
provider
end
- let(:nevra_format) { %Q{'%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH} :DESC: %{SUMMARY}\\n'} }
+ let(:nevra_format) { %Q{%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH} :DESC: %{SUMMARY}\\n} }
let(:execute_options) do
{:failonfail => true, :combine => true, :custom_environment => {}}
end
@@ -47,7 +48,7 @@ describe provider_class do
describe "self.instances" do
describe "with a modern version of RPM" do
it "should include all the modern flags" do
- Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf #{nevra_format}").yields(packages)
+ Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf '#{nevra_format}'").yields(packages)
installed_packages = subject.instances
end
@@ -56,7 +57,7 @@ describe provider_class do
describe "with a version of RPM < 4.1" do
let(:rpm_version) { "RPM version 4.0.2\n" }
it "should exclude the --nosignature flag" do
- Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nodigest --qf #{nevra_format}").yields(packages)
+ Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nodigest --qf '#{nevra_format}'").yields(packages)
installed_packages = subject.instances
end
@@ -65,14 +66,14 @@ describe provider_class do
describe "with a version of RPM < 4.0.2" do
let(:rpm_version) { "RPM version 3.0.5\n" }
it "should exclude the --nodigest flag" do
- Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --qf #{nevra_format}").yields(packages)
+ Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --qf '#{nevra_format}'").yields(packages)
installed_packages = subject.instances
end
end
it "returns an array of packages" do
- Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf #{nevra_format}").yields(packages)
+ Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf '#{nevra_format}'").yields(packages)
installed_packages = subject.instances
@@ -138,23 +139,40 @@ describe provider_class do
describe "when not already installed" do
it "should only include the '-i' flag" do
- Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-i", '/path/to/package'], execute_options)
+ Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-i"], '/path/to/package'], execute_options)
provider.install
end
- end
+ end
- describe "when an older version is installed" do
- before(:each) do
- # Force the provider to think a version of the package is already installed
- # This is real hacky. I'm sorry. --jeffweiss 25 Jan 2013
- provider.instance_variable_get('@property_hash')[:ensure] = '1.2.3.3'
- end
+ describe "when installed with options" do
+ let(:resource) do
+ Puppet::Type.type(:package).new(
+ :name => resource_name,
+ :ensure => :installed,
+ :provider => 'rpm',
+ :source => '/path/to/package',
+ :install_options => ['-D', {'--test' => 'value'}, '-Q']
+ )
+ end
- it "should include the '-U --oldpackage' flags" do
- Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-U", "--oldpackage"], '/path/to/package'], execute_options)
+ it "should include the options" do
+ Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-i", "-D", "--test=value", "-Q"], '/path/to/package'], execute_options)
provider.install
- end
- end
+ end
+ end
+
+ describe "when an older version is installed" do
+ before(:each) do
+ # Force the provider to think a version of the package is already installed
+ # This is real hacky. I'm sorry. --jeffweiss 25 Jan 2013
+ provider.instance_variable_get('@property_hash')[:ensure] = '1.2.3.3'
+ end
+
+ it "should include the '-U --oldpackage' flags" do
+ Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-U", "--oldpackage"], '/path/to/package'], execute_options)
+ provider.install
+ end
+ end
end
describe "#latest" do
@@ -268,6 +286,31 @@ describe provider_class do
end
end
+ describe "#install_options" do
+ it "should return empty array by default" do
+ provider.install_options.should == []
+ end
+
+ it "should return install_options when set" do
+ provider.resource[:install_options] = ['-n']
+ provider.install_options.should == ['-n']
+ end
+
+ it "should return multiple install_options when set" do
+ provider.resource[:install_options] = ['-L', '/opt/puppet']
+ provider.install_options.should == ['-L', '/opt/puppet']
+ end
+
+ it 'should return install_options when set as hash' do
+ provider.resource[:install_options] = { '-Darch' => 'vax' }
+ provider.install_options.should == ['-Darch=vax']
+ end
+ it 'should return install_options when an array with hashes' do
+ provider.resource[:install_options] = [ '-L', { '-Darch' => 'vax' }]
+ provider.install_options.should == ['-L', '-Darch=vax']
+ end
+ end
+
describe ".nodigest" do
{ '4.0' => nil,
'4.0.1' => nil,
diff --git a/spec/unit/provider/package/windows_spec.rb b/spec/unit/provider/package/windows_spec.rb
index d07d584de..4541f9ea7 100755
--- a/spec/unit/provider/package/windows_spec.rb
+++ b/spec/unit/provider/package/windows_spec.rb
@@ -14,8 +14,7 @@ describe Puppet::Type.type(:package).provider(:windows) do
end
def expect_execute(command, status)
- provider.expects(:execute).with(command, execute_options)
- provider.expects(:exit_status).returns(status)
+ provider.expects(:execute).with(command, execute_options).returns(Puppet::Util::Execution::ProcessOutput.new('',status))
end
describe 'provider features' do
@@ -23,6 +22,7 @@ describe Puppet::Type.type(:package).provider(:windows) do
it { should be_uninstallable }
it { should be_install_options }
it { should be_uninstall_options }
+ it { should be_versionable }
end
describe 'on Windows', :if => Puppet.features.microsoft_windows? do
@@ -36,17 +36,19 @@ describe Puppet::Type.type(:package).provider(:windows) do
pkg1 = stub('pkg1')
pkg2 = stub('pkg2')
- prov1 = stub('prov1', :name => 'pkg1', :package => pkg1)
- prov2 = stub('prov2', :name => 'pkg2', :package => pkg2)
+ prov1 = stub('prov1', :name => 'pkg1', :version => '1.0.0', :package => pkg1)
+ prov2 = stub('prov2', :name => 'pkg2', :version => nil, :package => pkg2)
Puppet::Provider::Package::Windows::Package.expects(:map).multiple_yields([prov1], [prov2]).returns([prov1, prov2])
providers = provider.class.instances
providers.count.should == 2
providers[0].name.should == 'pkg1'
+ providers[0].version.should == '1.0.0'
providers[0].package.should == pkg1
providers[1].name.should == 'pkg2'
+ providers[1].version.should be_nil
providers[1].package.should == pkg2
end
@@ -59,13 +61,21 @@ describe Puppet::Type.type(:package).provider(:windows) do
context '#query' do
it 'should return the hash of the matched packaged' do
- pkg = mock(:name => 'pkg1')
+ pkg = mock(:name => 'pkg1', :version => nil)
pkg.expects(:match?).returns(true)
Puppet::Provider::Package::Windows::Package.expects(:find).yields(pkg)
provider.query.should == { :name => 'pkg1', :ensure => :installed, :provider => :windows }
end
+ it 'should include the version string when present' do
+ pkg = mock(:name => 'pkg1', :version => '1.0.0')
+ pkg.expects(:match?).returns(true)
+ Puppet::Provider::Package::Windows::Package.expects(:find).yields(pkg)
+
+ provider.query.should == { :name => 'pkg1', :ensure => '1.0.0', :provider => :windows }
+ end
+
it 'should return nil if no package was found' do
Puppet::Provider::Package::Windows::Package.expects(:find)
@@ -102,13 +112,6 @@ describe Puppet::Type.type(:package).provider(:windows) do
provider.install
end
- it 'should warn if the package requests a reboot' do
- expect_execute(command, 194)
- provider.expects(:warning).with('The package requested a reboot to finish the operation.')
-
- provider.install
- end
-
it 'should warn if reboot initiated' do
expect_execute(command, 1641)
provider.expects(:warning).with('The package installed successfully and the system is rebooting now.')
@@ -161,13 +164,6 @@ describe Puppet::Type.type(:package).provider(:windows) do
provider.uninstall
end
- it 'should warn if the package requests a reboot' do
- expect_execute(command, 194)
- provider.expects(:warning).with('The package requested a reboot to finish the operation.')
-
- provider.uninstall
- end
-
it 'should warn if reboot initiated' do
expect_execute(command, 1641)
provider.expects(:warning).with('The package uninstalled successfully and the system is rebooting now.')
diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb
index 7a019bea7..c5d89ae60 100755
--- a/spec/unit/provider/package/yum_spec.rb
+++ b/spec/unit/provider/package/yum_spec.rb
@@ -153,7 +153,7 @@ _pkg mysummaryless 0 1.2.3.4 5.el4 noarch
end
def expect_execpipe_to_provide_package_info_for_an_rpm_query
- Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf #{nevra_format}").yields(packages)
+ Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf '#{nevra_format}'").yields(packages)
end
def expect_python_yumhelper_call_to_return_latest_info
diff --git a/spec/unit/provider/service/base_spec.rb b/spec/unit/provider/service/base_spec.rb
index b78bb0134..9d6a7fdcc 100755
--- a/spec/unit/provider/service/base_spec.rb
+++ b/spec/unit/provider/service/base_spec.rb
@@ -34,7 +34,7 @@ describe "base service provider" do
end
before :each do
- File.unlink(flag) if File.exist?(flag)
+ Puppet::FileSystem::File.unlink(flag) if Puppet::FileSystem::File.exist?(flag)
end
it { should be }
diff --git a/spec/unit/provider/service/daemontools_spec.rb b/spec/unit/provider/service/daemontools_spec.rb
index c538a0ced..4c35e0586 100755
--- a/spec/unit/provider/service/daemontools_spec.rb
+++ b/spec/unit/provider/service/daemontools_spec.rb
@@ -99,9 +99,14 @@ describe provider_class do
end
describe "when enabling" do
- it "should create a symlink between daemon dir and service dir" do
- FileTest.stubs(:symlink?).returns(false)
- File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0)
+ it "should create a symlink between daemon dir and service dir", :if => Puppet.features.manages_symlinks? do
+ daemon_path = File.join(@daemondir, "myservice")
+ stub_daemon = stub(daemon_path, :symlink? => false)
+ Puppet::FileSystem::File.expects(:new).with(daemon_path).returns(stub_daemon)
+ service_path = File.join(@servicedir, "myservice")
+ mock_service = mock(service_path, :symlink? => false)
+ Puppet::FileSystem::File.expects(:new).with(service_path).returns(mock_service)
+ stub_daemon.expects(:symlink).returns(0)
@provider.enable
end
end
@@ -109,16 +114,19 @@ describe provider_class do
describe "when disabling" do
it "should remove the symlink between daemon dir and service dir" do
FileTest.stubs(:directory?).returns(false)
- FileTest.stubs(:symlink?).returns(true)
- File.expects(:unlink).with(File.join(@servicedir,"myservice"))
+ path = File.join(@servicedir,"myservice")
+ mocked_file = mock(path, :symlink? => true)
+ Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file)
+ Puppet::FileSystem::File.expects(:unlink).with(path)
@provider.stubs(:texecute).returns("")
@provider.disable
end
it "should stop the service" do
FileTest.stubs(:directory?).returns(false)
- FileTest.stubs(:symlink?).returns(true)
- File.stubs(:unlink)
+ mocked_file = mock('anything', :symlink? => true)
+ Puppet::FileSystem::File.expects(:new).returns(mocked_file)
+ Puppet::FileSystem::File.stubs(:unlink)
@provider.expects(:stop)
@provider.disable
end
@@ -134,7 +142,9 @@ describe provider_class do
[true, false].each do |t|
it "should return #{t} if the symlink exists" do
@provider.stubs(:status).returns(:stopped)
- FileTest.stubs(:symlink?).returns(t)
+ path = File.join(@servicedir,"myservice")
+ mocked_file = mock(path, :symlink? => t)
+ Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file)
@provider.enabled?.should == "#{t}".to_sym
end
diff --git a/spec/unit/provider/service/freebsd_spec.rb b/spec/unit/provider/service/freebsd_spec.rb
index 81e3fc1af..69b923f9a 100755
--- a/spec/unit/provider/service/freebsd_spec.rb
+++ b/spec/unit/provider/service/freebsd_spec.rb
@@ -62,13 +62,13 @@ OUTPUT
end
it "should enable only the selected service" do
- File.stubs(:exists?).with('/etc/rc.conf').returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with('/etc/rc.conf').returns(true)
File.stubs(:read).with('/etc/rc.conf').returns("openntpd_enable=\"NO\"\nntpd_enable=\"NO\"\n")
fh = stub 'fh'
File.stubs(:open).with('/etc/rc.conf', File::WRONLY).yields(fh)
fh.expects(:<<).with("openntpd_enable=\"NO\"\nntpd_enable=\"YES\"\n")
- File.stubs(:exists?).with('/etc/rc.conf.local').returns(false)
- File.stubs(:exists?).with('/etc/rc.conf.d/ntpd').returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).with('/etc/rc.conf.local').returns(false)
+ Puppet::FileSystem::File.stubs(:exist?).with('/etc/rc.conf.d/ntpd').returns(false)
@provider.rc_replace('ntpd', 'ntpd', 'YES')
end
diff --git a/spec/unit/provider/service/gentoo_spec.rb b/spec/unit/provider/service/gentoo_spec.rb
index dab315537..aa802430e 100755
--- a/spec/unit/provider/service/gentoo_spec.rb
+++ b/spec/unit/provider/service/gentoo_spec.rb
@@ -13,7 +13,9 @@ describe Puppet::Type.type(:service).provider(:gentoo) do
# The initprovider (parent of the gentoo provider) does a stat call
# before it even tries to execute an initscript. We use sshd in all the
# tests so make sure it is considered present.
- File.stubs(:stat).with('/etc/init.d/sshd')
+ sshd_path = '/etc/init.d/sshd'
+ stub_file = stub(sshd_path, :stat => stub('stat'))
+ Puppet::FileSystem::File.stubs(:new).with(sshd_path).returns stub_file
end
let :initscripts do
@@ -48,7 +50,6 @@ describe Puppet::Type.type(:service).provider(:gentoo) do
it "should get a list of services from /etc/init.d but exclude helper scripts" do
FileTest.expects(:directory?).with('/etc/init.d').returns true
- File.stubs(:symlink?).returns(false)
Dir.expects(:entries).with('/etc/init.d').returns initscripts
(initscripts - helperscripts).each do |script|
FileTest.expects(:executable?).with("/etc/init.d/#{script}").returns true
@@ -56,6 +57,8 @@ describe Puppet::Type.type(:service).provider(:gentoo) do
helperscripts.each do |script|
FileTest.expects(:executable?).with("/etc/init.d/#{script}").never
end
+
+ Puppet::FileSystem::File.stubs(:new).returns stub('file', :symlink? => false)
described_class.instances.map(&:name).should == [
'alsasound',
'bootmisc',
diff --git a/spec/unit/provider/service/init_spec.rb b/spec/unit/provider/service/init_spec.rb
index e88672b40..896f13ac1 100755
--- a/spec/unit/provider/service/init_spec.rb
+++ b/spec/unit/provider/service/init_spec.rb
@@ -62,12 +62,12 @@ describe Puppet::Type.type(:service).provider(:init) do
described_class.instances.should be_all { |provider| provider.get(:hasstatus) == true }
end
- it "should discard upstart jobs" do
+ it "should discard upstart jobs", :if => Puppet.features.manages_symlinks? do
not_init_service, *valid_services = @services
- File.stubs(:symlink?).returns false
- File.stubs(:symlink?).with("tmp/#{not_init_service}").returns(true)
- File.stubs(:readlink).with("tmp/#{not_init_service}").returns("/lib/init/upstart-job")
-
+ path = "tmp/#{not_init_service}"
+ mocked_file = mock(path, :symlink? => true, :readlink => "/lib/init/upstart-job")
+ Puppet::FileSystem::File.stubs(:new).returns stub('file', :symlink? => false)
+ Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file)
described_class.instances.map(&:name).should == valid_services
end
@@ -82,7 +82,7 @@ describe Puppet::Type.type(:service).provider(:init) do
describe "when checking valid paths" do
it "should discard paths that do not exist" do
File.expects(:directory?).with(paths[0]).returns false
- File.expects(:exist?).with(paths[0]).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(paths[0]).returns false
File.expects(:directory?).with(paths[1]).returns true
provider.paths.should == [paths[1]]
@@ -90,7 +90,7 @@ describe Puppet::Type.type(:service).provider(:init) do
it "should discard paths that are not directories" do
paths.each do |path|
- File.expects(:exist?).with(path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns true
File.expects(:directory?).with(path).returns false
end
provider.paths.should be_empty
@@ -103,28 +103,28 @@ describe Puppet::Type.type(:service).provider(:init) do
end
it "should be able to find the init script in the service path" do
- File.expects(:exist?).with("#{paths[0]}/myservice").returns true
- File.expects(:exist?).with("#{paths[1]}/myservice").never # first one wins
+ Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice").returns true
+ Puppet::FileSystem::File.expects(:exist?).with("#{paths[1]}/myservice").never # first one wins
provider.initscript.should == "/service/path/myservice"
end
it "should be able to find the init script in an alternate service path" do
- File.expects(:exist?).with("#{paths[0]}/myservice").returns false
- File.expects(:exist?).with("#{paths[1]}/myservice").returns true
+ Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("#{paths[1]}/myservice").returns true
provider.initscript.should == "/alt/service/path/myservice"
end
it "should be able to find the init script if it ends with .sh" do
- File.expects(:exist?).with("#{paths[0]}/myservice").returns false
- File.expects(:exist?).with("#{paths[1]}/myservice").returns false
- File.expects(:exist?).with("#{paths[0]}/myservice.sh").returns true
+ Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("#{paths[1]}/myservice").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("#{paths[0]}/myservice.sh").returns true
provider.initscript.should == "/service/path/myservice.sh"
end
it "should fail if the service isn't there" do
paths.each do |path|
- File.expects(:exist?).with("#{path}/myservice").returns false
- File.expects(:exist?).with("#{path}/myservice.sh").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("#{path}/myservice").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("#{path}/myservice.sh").returns false
end
expect { provider.initscript }.to raise_error(Puppet::Error, "Could not find init script for 'myservice'")
end
@@ -134,7 +134,7 @@ describe Puppet::Type.type(:service).provider(:init) do
before :each do
File.stubs(:directory?).with("/service/path").returns true
File.stubs(:directory?).with("/alt/service/path").returns true
- File.stubs(:exist?).with("/service/path/myservice").returns true
+ Puppet::FileSystem::File.stubs(:exist?).with("/service/path/myservice").returns true
end
[:start, :stop, :status, :restart].each do |method|
diff --git a/spec/unit/provider/service/launchd_spec.rb b/spec/unit/provider/service/launchd_spec.rb
index fc82fdd00..b126fb22d 100755
--- a/spec/unit/provider/service/launchd_spec.rb
+++ b/spec/unit/provider/service/launchd_spec.rb
@@ -209,29 +209,82 @@ describe Puppet::Type.type(:service).provider(:launchd) do
end
end
- describe "when encountering malformed plists" do
- let(:plist_without_label) do
- {
- 'LimitLoadToSessionType' => 'Aqua'
- }
- end
- let(:busted_plist_path) { '/Library/LaunchAgents/org.busted.plist' }
-
- it "[17624] should warn that the plist in question is being skipped" do
- provider.expects(:launchd_paths).returns(['/Library/LaunchAgents'])
- provider.expects(:return_globbed_list_of_file_paths).with('/Library/LaunchAgents').returns([busted_plist_path])
- provider.expects(:read_plist).with(busted_plist_path).returns(plist_without_label)
- Puppet.expects(:warning).with("The #{busted_plist_path} plist does not contain a 'label' key; Puppet is skipping it")
- provider.jobsearch
- end
-
- it "[15929] should skip plists that plutil cannot read" do
- provider.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout',
- busted_plist_path).raises(Puppet::ExecutionFailure, 'boom')
- Puppet.expects(:warning).with("Cannot read file #{busted_plist_path}; " +
- "Puppet is skipping it. \n" +
- "Details: boom")
- provider.read_plist(busted_plist_path)
+ describe "make_label_to_path_map" do
+ before do
+ # clear out this class variable between runs
+ if provider.instance_variable_defined? :@label_to_path_map
+ provider.send(:remove_instance_variable, :@label_to_path_map)
+ end
+ end
+ describe "when encountering malformed plists" do
+ let(:plist_without_label) do
+ {
+ 'LimitLoadToSessionType' => 'Aqua'
+ }
+ end
+ let(:busted_plist_path) { '/Library/LaunchAgents/org.busted.plist' }
+
+ it "[17624] should warn that the plist in question is being skipped" do
+ provider.expects(:launchd_paths).returns(['/Library/LaunchAgents'])
+ provider.expects(:return_globbed_list_of_file_paths).with('/Library/LaunchAgents').returns([busted_plist_path])
+ provider.expects(:read_plist).with(busted_plist_path).returns(plist_without_label)
+ Puppet.expects(:warning).with("The #{busted_plist_path} plist does not contain a 'label' key; Puppet is skipping it")
+ provider.make_label_to_path_map
+ end
+
+ it "[15929] should skip plists that plutil cannot read" do
+ provider.expects(:plutil).with('-convert', 'xml1', '-o', '/dev/stdout',
+ busted_plist_path).raises(Puppet::ExecutionFailure, 'boom')
+ Puppet.expects(:warning).with("Cannot read file #{busted_plist_path}; " +
+ "Puppet is skipping it. \n" +
+ "Details: boom")
+ provider.read_plist(busted_plist_path)
+ end
+ end
+ it "should return the cached value when available" do
+ provider.instance_variable_set(:@label_to_path_map, {'xx'=>'yy'})
+ provider.make_label_to_path_map.should eq({'xx'=>'yy'})
+ end
+ describe "when successful" do
+ let(:launchd_dir) { '/Library/LaunchAgents' }
+ let(:plist) { launchd_dir + '/foo.bar.service.plist' }
+ let(:label) { 'foo.bar.service' }
+ before do
+ provider.instance_variable_set(:@label_to_path_map, nil)
+ provider.expects(:launchd_paths).returns([launchd_dir])
+ provider.expects(:return_globbed_list_of_file_paths).with(launchd_dir).returns([plist])
+ provider.expects(:read_plist).with(plist).returns({'Label'=>'foo.bar.service'})
+ end
+ it "should read the plists and return their contents" do
+ provider.make_label_to_path_map.should eq({label=>plist})
+ end
+ it "should re-read the plists and return their contents when refreshed" do
+ provider.instance_variable_set(:@label_to_path_map, {'xx'=>'yy'})
+ provider.make_label_to_path_map(true).should eq({label=>plist})
+ end
+ end
+ end
+
+ describe "jobsearch" do
+ let(:map) { {"org.mozilla.puppet" => "/path/to/puppet.plist",
+ "org.mozilla.python" => "/path/to/python.plist"} }
+ it "returns the entire map with no args" do
+ provider.expects(:make_label_to_path_map).returns(map)
+ provider.jobsearch.should == map
+ end
+ it "returns a singleton hash when given a label" do
+ provider.expects(:make_label_to_path_map).returns(map)
+ provider.jobsearch("org.mozilla.puppet").should == { "org.mozilla.puppet" => "/path/to/puppet.plist" }
+ end
+ it "refreshes the label_to_path_map when label is not found" do
+ provider.expects(:make_label_to_path_map).with().returns({})
+ provider.expects(:make_label_to_path_map).with(true).returns(map)
+ provider.jobsearch("org.mozilla.puppet").should == { "org.mozilla.puppet" => "/path/to/puppet.plist" }
+ end
+ it "raises Puppet::Error when the label is still not found" do
+ provider.expects(:make_label_to_path_map).with().returns(map)
+ provider.expects(:make_label_to_path_map).with(true).returns(map)
+ expect { provider.jobsearch("NOSUCH") }.to raise_error(Puppet::Error)
end
end
end
diff --git a/spec/unit/provider/service/openbsd_spec.rb b/spec/unit/provider/service/openbsd_spec.rb
new file mode 100644
index 000000000..8c417986d
--- /dev/null
+++ b/spec/unit/provider/service/openbsd_spec.rb
@@ -0,0 +1,125 @@
+#!/usr/bin/env ruby
+#
+# Unit testing for the OpenBSD service provider
+
+require 'spec_helper'
+
+provider_class = Puppet::Type.type(:service).provider(:openbsd)
+
+describe provider_class do
+ before :each do
+ Puppet::Type.type(:service).stubs(:defaultprovider).returns described_class
+ Facter.stubs(:value).with(:operatingsystem).returns :openbsd
+ end
+
+ let :rcscripts do
+ [
+ 'apmd',
+ 'aucat',
+ 'cron',
+ 'puppetd'
+ ]
+ end
+
+ describe "#instances" do
+ it "should have an instances method" do
+ described_class.should respond_to :instances
+ end
+
+ it "should list all available services" do
+ FileTest.expects(:directory?).with('/etc/rc.d').returns true
+ Dir.expects(:entries).with('/etc/rc.d').returns rcscripts
+
+ rcscripts.each do |script|
+ FileTest.expects(:executable?).with("/etc/rc.d/#{script}").returns true
+ end
+
+ described_class.instances.map(&:name).should == [
+ 'apmd',
+ 'aucat',
+ 'cron',
+ 'puppetd'
+ ]
+ end
+ end
+
+ describe "#start" do
+ it "should use the supplied start command if specified" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :start => '/bin/foo'))
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.start
+ end
+
+ it "should start the service otherwise" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :start], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd')
+ provider.start
+ end
+ end
+
+ describe "#stop" do
+ it "should use the supplied stop command if specified" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :stop => '/bin/foo'))
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.stop
+ end
+
+ it "should stop the service otherwise" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd'))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', :stop], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd')
+ provider.stop
+ end
+ end
+
+ describe "#status" do
+ it "should use the status command from the resource" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true)
+ provider.status
+ end
+
+ it "should return :stopped when status command returns with a non-zero exitcode" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true)
+ $CHILD_STATUS.stubs(:exitstatus).returns 3
+ provider.status.should == :stopped
+ end
+
+ it "should return :running when status command returns with a zero exitcode" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :status => '/bin/foo'))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', :status], :failonfail => false, :override_locale => false, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => false, :override_locale => false, :squelch => true)
+ $CHILD_STATUS.stubs(:exitstatus).returns 0
+ provider.status.should == :running
+ end
+ end
+
+ describe "#restart" do
+ it "should use the supplied restart command if specified" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :restart => '/bin/foo'))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => true).never
+ provider.expects(:execute).with(['/bin/foo'], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.restart
+ end
+
+ it "should restart the service with rc-service restart if hasrestart is true" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => true))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd')
+ provider.restart
+ end
+
+ it "should restart the service with rc-service stop/start if hasrestart is false" do
+ provider = described_class.new(Puppet::Type.type(:service).new(:name => 'sshd', :hasrestart => false))
+ provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :restart], :failonfail => true, :override_locale => false, :squelch => true).never
+ provider.expects(:execute).with(['/etc/rc.d/sshd', :stop], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.expects(:execute).with(['/etc/rc.d/sshd', '-f', :start], :failonfail => true, :override_locale => false, :squelch => true)
+ provider.expects(:search).with('sshd').returns('/etc/rc.d/sshd')
+ provider.restart
+ end
+ end
+end
diff --git a/spec/unit/provider/service/openwrt_spec.rb b/spec/unit/provider/service/openwrt_spec.rb
index bc8a6323d..2113aceb4 100755
--- a/spec/unit/provider/service/openwrt_spec.rb
+++ b/spec/unit/provider/service/openwrt_spec.rb
@@ -33,7 +33,7 @@ describe Puppet::Type.type(:service).provider(:openwrt), :as_platform => :posix
# All OpenWrt tests operate on the init script directly. It must exist.
File.stubs(:directory?).with('/etc/init.d').returns true
- File.stubs(:exist?).with('/etc/init.d/myservice').returns true
+ Puppet::FileSystem::File.stubs(:exist?).with('/etc/init.d/myservice').returns true
FileTest.stubs(:file?).with('/etc/init.d/myservice').returns true
FileTest.stubs(:executable?).with('/etc/init.d/myservice').returns true
end
diff --git a/spec/unit/provider/service/runit_spec.rb b/spec/unit/provider/service/runit_spec.rb
index 5c5b37b37..cbdc19ba6 100755
--- a/spec/unit/provider/service/runit_spec.rb
+++ b/spec/unit/provider/service/runit_spec.rb
@@ -96,18 +96,25 @@ describe provider_class do
end
describe "when enabling" do
- it "should create a symlink between daemon dir and service dir" do
- FileTest.stubs(:symlink?).returns(false)
- File.expects(:symlink).with(File.join(@daemondir,"myservice"), File.join(@servicedir,"myservice")).returns(0)
+ it "should create a symlink between daemon dir and service dir", :if => Puppet.features.manages_symlinks? do
+ daemon_path = File.join(@daemondir,"myservice")
+ mock_daemon = mock(daemon_path)
+ Puppet::FileSystem::File.expects(:new).with(daemon_path).returns(mock_daemon)
+ service_path = File.join(@servicedir,"myservice")
+ mock_service = mock(service_path, :symlink? => false)
+ Puppet::FileSystem::File.expects(:new).with(service_path).returns(mock_service)
+ mock_daemon.expects(:symlink).with(File.join(@servicedir,"myservice")).returns(0)
@provider.enable
end
end
describe "when disabling" do
it "should remove the '/etc/service/myservice' symlink" do
+ path = File.join(@servicedir,"myservice")
+ mocked_file = mock(path, :symlink? => true)
FileTest.stubs(:directory?).returns(false)
- FileTest.stubs(:symlink?).returns(true)
- File.expects(:unlink).with(File.join(@servicedir,"myservice")).returns(0)
+ Puppet::FileSystem::File.expects(:new).with(path).returns(mocked_file)
+ Puppet::FileSystem::File.expects(:unlink).with(path).returns(0)
@provider.disable
end
end
diff --git a/spec/unit/provider/service/upstart_spec.rb b/spec/unit/provider/service/upstart_spec.rb
index e24857901..ed93386b3 100755
--- a/spec/unit/provider/service/upstart_spec.rb
+++ b/spec/unit/provider/service/upstart_spec.rb
@@ -51,8 +51,8 @@ describe Puppet::Type.type(:service).provider(:upstart) do
describe "#search" do
it "searches through paths to find a matching conf file" do
File.stubs(:directory?).returns(true)
- File.stubs(:exists?).returns(false)
- File.expects(:exists?).with("/etc/init/foo-bar.conf").returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with("/etc/init/foo-bar.conf").returns(true)
resource = Puppet::Type.type(:service).new(:name => "foo-bar", :provider => :upstart)
provider = provider_class.new(resource)
@@ -61,8 +61,8 @@ describe Puppet::Type.type(:service).provider(:upstart) do
it "searches for just the name of a compound named service" do
File.stubs(:directory?).returns(true)
- File.stubs(:exists?).returns(false)
- File.expects(:exists?).with("/etc/init/network-interface.conf").returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with("/etc/init/network-interface.conf").returns(true)
resource = Puppet::Type.type(:service).new(:name => "network-interface INTERFACE=lo", :provider => :upstart)
provider = provider_class.new(resource)
diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb
index 5cfa8994b..4d17ffe51 100755
--- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb
+++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb
@@ -162,7 +162,7 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do
end
it "should create the directory" do
- File.stubs(:exist?).with("/tmp/.ssh_dir").returns false
+ Puppet::FileSystem::File.stubs(:exist?).with("/tmp/.ssh_dir").returns false
Dir.expects(:mkdir).with("/tmp/.ssh_dir", 0700)
@provider.flush
end
@@ -199,19 +199,19 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do
end
it "should create the directory if it doesn't exist" do
- File.stubs(:exist?).with(@dir).returns false
+ Puppet::FileSystem::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
+ Puppet::FileSystem::File.stubs(:exist?).with(@dir).returns false
Dir.expects(:mkdir).never
@provider.flush
end
it "should absolutely not chown the directory to the user if it creates it" do
- File.stubs(:exist?).with(@dir).returns false
+ Puppet::FileSystem::File.stubs(:exist?).with(@dir).returns false
Dir.stubs(:mkdir).with(@dir,0700)
uid = Puppet::Util.uid("nobody")
File.expects(:chown).never
@@ -219,7 +219,7 @@ describe provider_class, :unless => Puppet.features.microsoft_windows? do
end
it "should not create or chown the directory if it already exist" do
- File.stubs(:exist?).with(@dir).returns false
+ Puppet::FileSystem::File.stubs(:exist?).with(@dir).returns false
Dir.expects(:mkdir).never
File.expects(:chown).never
@provider.flush
diff --git a/spec/unit/provider/user/directoryservice_spec.rb b/spec/unit/provider/user/directoryservice_spec.rb
index e464e2c6f..fe72bfc4f 100755
--- a/spec/unit/provider/user/directoryservice_spec.rb
+++ b/spec/unit/provider/user/directoryservice_spec.rb
@@ -704,7 +704,7 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
let(:stub_password_file) { stub('connection') }
it 'should return a sha1 hash read from disk' do
- File.expects(:exists?).with(password_hash_file).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(true)
File.expects(:file?).with(password_hash_file).returns(true)
File.expects(:readable?).with(password_hash_file).returns(true)
File.expects(:new).with(password_hash_file).returns(stub_password_file)
@@ -714,18 +714,18 @@ describe Puppet::Type.type(:user).provider(:directoryservice) do
end
it 'should return nil if the password_hash_file does not exist' do
- File.expects(:exists?).with(password_hash_file).returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(false)
provider.class.get_sha1('user_guid').should == nil
end
it 'should return nil if the password_hash_file is not a file' do
- File.expects(:exists?).with(password_hash_file).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(true)
File.expects(:file?).with(password_hash_file).returns(false)
provider.class.get_sha1('user_guid').should == nil
end
it 'should raise an error if the password_hash_file is not readable' do
- File.expects(:exists?).with(password_hash_file).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(password_hash_file).returns(true)
File.expects(:file?).with(password_hash_file).returns(true)
File.expects(:readable?).with(password_hash_file).returns(false)
expect { provider.class.get_sha1('user_guid').should == nil }.to raise_error Puppet::Error, /Could not read password hash file at #{password_hash_file}/
diff --git a/spec/unit/provider/user/windows_adsi_spec.rb b/spec/unit/provider/user/windows_adsi_spec.rb
index 4e3a6f463..c25ccaf95 100755
--- a/spec/unit/provider/user/windows_adsi_spec.rb
+++ b/spec/unit/provider/user/windows_adsi_spec.rb
@@ -24,7 +24,7 @@ describe Puppet::Type.type(:user).provider(:windows_adsi) do
it "should enumerate all users" do
names = ['user1', 'user2', 'user3']
stub_users = names.map{|n| stub(:name => n)}
- connection.stubs(:execquery).with("select name from win32_useraccount").returns(stub_users)
+ connection.stubs(:execquery).with('select name from win32_useraccount where localaccount = "TRUE"').returns(stub_users)
described_class.instances.map(&:name).should =~ names
end
diff --git a/spec/unit/provider/zone/solaris_spec.rb b/spec/unit/provider/zone/solaris_spec.rb
index 6fd58a46a..c143cabd1 100755
--- a/spec/unit/provider/zone/solaris_spec.rb
+++ b/spec/unit/provider/zone/solaris_spec.rb
@@ -138,7 +138,7 @@ net:
it "should not require path if sysidcfg is specified" do
resource[:path] = '/mypath'
resource[:sysidcfg] = 'dummy'
- File.stubs(:exists?).with('/mypath/root/etc/sysidcfg').returns true
+ Puppet::FileSystem::File.stubs(:exist?).with('/mypath/root/etc/sysidcfg').returns true
File.stubs(:directory?).with('/mypath/root/etc').returns true
provider.expects(:zoneadm).with(:boot)
provider.start
diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb
index 208094b7e..31e69f799 100755
--- a/spec/unit/provider_spec.rb
+++ b/spec/unit/provider_spec.rb
@@ -349,10 +349,10 @@ describe Puppet::Provider do
command = Puppet::Util.which('sh') || Puppet::Util.which('cmd.exe')
parent.commands :sh => command
- FileTest.should be_exists parent.command(:sh)
+ Puppet::FileSystem::File.exist?(parent.command(:sh)).should be_true
parent.command(:sh).should =~ /#{Regexp.escape(command)}$/
- FileTest.should be_exists child.command(:sh)
+ Puppet::FileSystem::File.exist?(child.command(:sh)).should be_true
child.command(:sh).should =~ /#{Regexp.escape(command)}$/
end
diff --git a/spec/unit/reports/http_spec.rb b/spec/unit/reports/http_spec.rb
index 13f202607..1a99761d8 100755
--- a/spec/unit/reports/http_spec.rb
+++ b/spec/unit/reports/http_spec.rb
@@ -15,65 +15,50 @@ describe processor do
http.expects(:post).returns(httpok)
end
- it "should use the reporturl setting's host, port and ssl option" do
- uri = URI.parse(Puppet[:reporturl])
- ssl = (uri.scheme == 'https')
- Net::HTTP.expects(:new).with(
- uri.host, uri.port, optionally(anything, anything)
- ).returns http
- http.expects(:use_ssl=).with(ssl)
- subject.process
- end
+ it "configures the connection for ssl when using https" do
+ Puppet[:reporturl] = 'https://testing:8080/the/path'
- it "uses ssl if reporturl has the https protocol" do
- Puppet[:reporturl] = "https://myhost.mydomain:1234/report/upload"
- uri = URI.parse(Puppet[:reporturl])
- Net::HTTP.expects(:new).with(
- uri.host, uri.port, optionally(anything, anything)
+ Puppet::Network::HttpPool.expects(:http_instance).with(
+ 'testing', 8080, true
).returns http
- http.expects(:use_ssl=).with(true)
+
subject.process
end
- it "does not use ssl if reporturl has plain http protocol" do
- Puppet[:reporturl] = "http://myhost.mydomain:1234/report/upload"
- uri = URI.parse(Puppet[:reporturl])
- Net::HTTP.expects(:new).with(
- uri.host, uri.port, optionally(anything, anything)
+ it "does not configure the connectino for ssl when using http" do
+ Puppet[:reporturl] = "http://testing:8080/the/path"
+
+ Puppet::Network::HttpPool.expects(:http_instance).with(
+ 'testing', 8080, false
).returns http
- http.expects(:use_ssl=).with(false)
+
subject.process
end
end
describe "when making a request" do
- let(:http) { stub_everything "http" }
+ let(:connection) { stub_everything "connection" }
let(:httpok) { Net::HTTPOK.new('1.1', 200, '') }
before :each do
- Net::HTTP.expects(:new).returns(http)
+ Puppet::Network::HttpPool.expects(:http_instance).returns(connection)
end
it "should use the path specified by the 'reporturl' setting" do
- http.expects(:post).with {|path, data, headers|
- path.should == URI.parse(Puppet[:reporturl]).path
- }.returns(httpok)
+ report_path = URI.parse(Puppet[:reporturl]).path
+ connection.expects(:post).with(report_path, anything, anything).returns(httpok)
subject.process
end
it "should give the body as the report as YAML" do
- http.expects(:post).with {|path, data, headers|
- data.should == subject.to_yaml
- }.returns(httpok)
+ connection.expects(:post).with(anything, subject.to_yaml, anything).returns(httpok)
subject.process
end
it "should set content-type to 'application/x-yaml'" do
- http.expects(:post).with {|path, data, headers|
- headers["Content-Type"].should == "application/x-yaml"
- }.returns(httpok)
+ connection.expects(:post).with(anything, anything, has_entry("Content-Type" => "application/x-yaml")).returns(httpok)
subject.process
end
@@ -82,7 +67,7 @@ describe processor do
if code.to_i >= 200 and code.to_i < 300
it "should succeed on http code #{code}" do
response = klass.new('1.1', code, '')
- http.expects(:post).returns(response)
+ connection.expects(:post).returns(response)
Puppet.expects(:err).never
subject.process
@@ -92,7 +77,7 @@ describe processor do
if code.to_i >= 300 && ![301, 302, 307].include?(code.to_i)
it "should log error on http code #{code}" do
response = klass.new('1.1', code, '')
- http.expects(:post).returns(response)
+ connection.expects(:post).returns(response)
Puppet.expects(:err)
subject.process
diff --git a/spec/unit/reports/store_spec.rb b/spec/unit/reports/store_spec.rb
index 5320c6479..421f8404d 100755
--- a/spec/unit/reports/store_spec.rb
+++ b/spec/unit/reports/store_spec.rb
@@ -47,7 +47,7 @@ describe processor do
it "rejects invalid hostnames" do
@report.host = ".."
- FileTest.expects(:exists?).never
+ Puppet::FileSystem::File.expects(:exist?).never
Tempfile.expects(:new).never
expect { @report.process }.to raise_error(ArgumentError, /Invalid node/)
end
@@ -55,7 +55,7 @@ describe processor do
describe "::destroy" do
it "rejects invalid hostnames" do
- File.expects(:unlink).never
+ Puppet::FileSystem::File.expects(:unlink).never
expect { processor.destroy("..") }.to raise_error(ArgumentError, /Invalid node/)
end
end
diff --git a/spec/unit/resource/catalog_spec.rb b/spec/unit/resource/catalog_spec.rb
index dab6e4591..dd88e8e20 100755
--- a/spec/unit/resource/catalog_spec.rb
+++ b/spec/unit/resource/catalog_spec.rb
@@ -1,5 +1,18 @@
#! /usr/bin/env ruby
require 'spec_helper'
+require 'puppet_spec/compiler'
+
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ CATALOG_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/catalog.json')))
+
+ describe "catalog schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, CATALOG_SCHEMA)
+ end
+ end
+
+end
describe Puppet::Resource::Catalog, "when compiling" do
include PuppetSpec::Files
@@ -86,26 +99,28 @@ describe Puppet::Resource::Catalog, "when compiling" do
it "should accept tags" do
config = Puppet::Resource::Catalog.new("mynode")
config.tag("one")
- config.tags.should == %w{one}
+ config.should be_tagged("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}
+ config.should be_tagged("one")
+ config.should be_tagged("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}
+ config.should be_tagged("one")
+ config.should be_tagged("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
+ config.should be_tagged("one")
+ config.should be_tagged("one::two")
end
it "should accept classes" do
@@ -119,7 +134,7 @@ describe Puppet::Resource::Catalog, "when compiling" do
it "should tag itself with passed class names" do
config = Puppet::Resource::Catalog.new("mynode")
config.add_class("one")
- config.tags.should == %w{one}
+ config.should be_tagged("one")
end
end
@@ -204,12 +219,12 @@ describe Puppet::Resource::Catalog, "when compiling" do
@r1 = stub_everything 'r1', :ref => "File[/a]"
@r1.stubs(:respond_to?).with(:ref).returns(true)
- @r1.stubs(:dup).returns(@r1)
+ @r1.stubs(:copy_as_resource).returns(@r1)
@r1.stubs(:is_a?).with(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(:copy_as_resource).returns(@r2)
@r2.stubs(:is_a?).with(Puppet::Resource).returns(true)
@resources = [@r1,@r2]
@@ -720,6 +735,60 @@ describe Puppet::Resource::Catalog, "when compiling" do
end
end
+describe Puppet::Resource::Catalog, "when converting a resource catalog to pson" do
+ include PuppetSpec::Compiler
+
+ def validate_json_for_catalog(catalog)
+ JSON::Validator.validate!(CATALOG_SCHEMA, catalog.to_pson)
+ end
+
+ it "should validate an empty catalog against the schema", :unless => Puppet.features.microsoft_windows? do
+ empty_catalog = compile_to_catalog("")
+ validate_json_for_catalog(empty_catalog)
+ end
+
+ it "should validate a noop catalog against the schema", :unless => Puppet.features.microsoft_windows? do
+ noop_catalog = compile_to_catalog("create_resources('file', {})")
+ validate_json_for_catalog(noop_catalog)
+ end
+
+ it "should validate a single resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do
+ catalog = compile_to_catalog("create_resources('file', {'/etc/foo'=>{'ensure'=>'present'}})")
+ validate_json_for_catalog(catalog)
+ end
+
+ it "should validate a virtual resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do
+ catalog = compile_to_catalog("create_resources('@file', {'/etc/foo'=>{'ensure'=>'present'}})\nrealize(File['/etc/foo'])")
+ validate_json_for_catalog(catalog)
+ end
+
+ it "should validate a single exported resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do
+ catalog = compile_to_catalog("create_resources('@@file', {'/etc/foo'=>{'ensure'=>'present'}})")
+ validate_json_for_catalog(catalog)
+ end
+
+ it "should validate a two resource catalog against the schema", :unless => Puppet.features.microsoft_windows? do
+ catalog = compile_to_catalog("create_resources('notify', {'foo'=>{'message'=>'one'}, 'bar'=>{'message'=>'two'}})")
+ validate_json_for_catalog(catalog)
+ end
+
+ it "should validate a two parameter class catalog against the schema", :unless => Puppet.features.microsoft_windows? do
+ catalog = compile_to_catalog(<<-MANIFEST)
+ class multi_param_class ($one, $two) {
+ notify {'foo':
+ message => "One is $one, two is $two",
+ }
+ }
+
+ class {'multi_param_class':
+ one => 'hello',
+ two => 'world',
+ }
+ MANIFEST
+ validate_json_for_catalog(catalog)
+ end
+end
+
describe Puppet::Resource::Catalog, "when converting to pson" do
before do
@catalog = Puppet::Resource::Catalog.new("myhost")
@@ -742,11 +811,11 @@ describe Puppet::Resource::Catalog, "when converting to pson" do
PSON.parse @catalog.to_pson
end
- [:name, :version, :tags, :classes].each do |param|
+ [:name, :version, :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_output_should { |hash| hash['data'][param.to_s].should == @catalog.send(param) }
PSON.parse @catalog.to_pson
end
end
@@ -815,7 +884,8 @@ describe Puppet::Resource::Catalog, "when converting from pson" do
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']
+ @catalog.should be_tagged("one")
+ @catalog.should be_tagged("two")
end
it "should set any provided classes on the catalog" do
diff --git a/spec/unit/resource/resource_type.json b/spec/unit/resource/resource_type.json
deleted file mode 100644
index ffd15d639..000000000
--- a/spec/unit/resource/resource_type.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
- "type": "object",
- "properties": {
- "doc": {
- "type": "string"
- },
- "line": {
- "type": "integer"
- },
- "file": {
- "type": "string"
- },
- "parent": {
- "type": "string"
- },
- "name": {
- "type": "string",
- "required": "true"
- },
- "kind": {
- "type": "string",
- "enum": [
- "class",
- "node",
- "defined_type"
- ],
- "required": "true"
- },
- "parameters": {
- "type": "object"
- }
- },
- "additionalProperties": false
-}
diff --git a/spec/unit/resource/status_spec.rb b/spec/unit/resource/status_spec.rb
index f3cc5699c..d50abdce3 100755
--- a/spec/unit/resource/status_spec.rb
+++ b/spec/unit/resource/status_spec.rb
@@ -59,7 +59,9 @@ describe Puppet::Resource::Status do
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}
+ status = Puppet::Resource::Status.new(@resource)
+ status.should be_tagged("foo")
+ status.should be_tagged("bar")
end
it "should always convert the resource to a string" do
@@ -113,6 +115,14 @@ describe Puppet::Resource::Status do
@status.events.should == [event]
end
+ it "records an event for a failure caused by an error" do
+ @status.failed_because(StandardError.new("the message"))
+
+ expect(@status.events[0].message).to eq("the message")
+ expect(@status.events[0].status).to eq("failure")
+ expect(@status.events[0].name).to eq(:resource_error)
+ 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
diff --git a/spec/unit/resource/type_spec.rb b/spec/unit/resource/type_spec.rb
index a4929208e..1b5a4727b 100755
--- a/spec/unit/resource/type_spec.rb
+++ b/spec/unit/resource/type_spec.rb
@@ -1,8 +1,19 @@
#! /usr/bin/env ruby
require 'spec_helper'
-
require 'puppet/resource/type'
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ RESOURCE_TYPE_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/resource_type.json')))
+
+ describe "resource type schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, RESOURCE_TYPE_SCHEMA)
+ end
+ end
+
+end
+
describe Puppet::Resource::Type do
it "should have a 'name' attribute" do
Puppet::Resource::Type.new(:hostclass, "foo").name.should == "foo"
@@ -31,6 +42,10 @@ describe Puppet::Resource::Type do
end
describe "when converting to json" do
+ def validate_json_for_type(type)
+ JSON::Validator.validate!(RESOURCE_TYPE_SCHEMA, type.to_pson)
+ end
+
before do
@type = Puppet::Resource::Type.new(:hostclass, "foo")
end
@@ -48,6 +63,20 @@ describe Puppet::Resource::Type do
double_convert.type.should == @type.type
end
+ it "should validate with only name and kind", :unless => Puppet.features.microsoft_windows? do
+ validate_json_for_type(@type)
+ end
+
+ it "should validate with all fields set", :unless => Puppet.features.microsoft_windows? do
+ @type.set_arguments("one" => nil, "two" => "foo")
+ @type.line = 100
+ @type.doc = "A weird type"
+ @type.file = "/etc/manifests/thing.pp"
+ @type.parent = "one::two"
+
+ validate_json_for_type(@type)
+ end
+
it "should include any arguments" do
@type.set_arguments("one" => nil, "two" => "foo")
diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb
index 37d2001b8..a8f21d205 100755
--- a/spec/unit/resource_spec.rb
+++ b/spec/unit/resource_spec.rb
@@ -339,8 +339,11 @@ describe Puppet::Resource do
end
it "should query the injector using a namespaced key" do
- compiler.injector.expects(:lookup).with(scope, 'apache::port')
+ compiler.injector.expects(:lookup).with(scope, 'apache::port').returns("8081")
+
resource.set_default_parameters(scope)
+
+ resource[:port].should == "8081"
end
it "should use the value from the data_binding terminus" do
@@ -376,8 +379,16 @@ describe Puppet::Resource do
resource[:port].should == '80'
end
+ it "should fail with error message about data binding on a hiera failure" do
+ Puppet::DataBinding.indirection.expects(:find).raises(Puppet::DataBinding::LookupError, 'Forgettabotit')
+ expect {
+ resource.set_default_parameters(scope)
+ }.to raise_error(Puppet::Error, /Error from DataBinding 'hiera' while looking up 'apache::port':.*Forgettabotit/)
+ end
+
it "should use the default value if the injector returns nil" do
compiler.injector.expects(:lookup).returns(nil)
+ Puppet::DataBinding.indirection.expects(:find).returns(nil)
resource.set_default_parameters(scope)
@@ -607,7 +618,7 @@ describe Puppet::Resource do
end
end
- describe "when serializing" do
+ describe "when serializing a native type" do
before do
@resource = Puppet::Resource.new("file", "/my/file")
@resource["one"] = "test"
@@ -622,6 +633,31 @@ describe Puppet::Resource do
end
end
+ describe "when serializing a defined type" do
+ before do
+ type = Puppet::Resource::Type.new(:definition, "foo::bar")
+ Puppet::Node::Environment.new.known_resource_types.add type
+ end
+
+ before :each do
+ @resource = Puppet::Resource.new('foo::bar', 'xyzzy')
+ @resource['one'] = 'test'
+ @resource['two'] = 'other'
+ @resource.resource_type
+ end
+
+ it "doesn't include transient instance variables (#4506)" do
+ expect(@resource.to_yaml_properties).to_not include :@rstype
+ end
+
+ it "produces an equivalent yaml object" do
+ text = @resource.render('yaml')
+
+ newresource = Puppet::Resource.convert_from('yaml', text)
+ newresource.should equal_attributes_of @resource
+ 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")
@@ -810,9 +846,9 @@ describe Puppet::Resource do
end
end
- describe "it should implement to_resource" do
+ describe "it should implement copy_as_resource" do
resource = Puppet::Resource.new("file", "/my/file")
- resource.to_resource.should == resource
+ resource.copy_as_resource.should == resource
end
describe "because it is an indirector model" do
diff --git a/spec/unit/settings/autosign_setting_spec.rb b/spec/unit/settings/autosign_setting_spec.rb
new file mode 100644
index 000000000..c08171374
--- /dev/null
+++ b/spec/unit/settings/autosign_setting_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+require 'puppet/settings'
+require 'puppet/settings/autosign_setting'
+
+describe Puppet::Settings::AutosignSetting do
+ let(:setting) { described_class.new(:settings => mock('settings'), :desc => "test") }
+
+ it "is of type :autosign" do
+ expect(setting.type).to eq :autosign
+ end
+
+ describe "when munging the setting" do
+ it "passes boolean values through" do
+ expect(setting.munge(true)).to eq true
+ expect(setting.munge(false)).to eq false
+ end
+
+ it "converts nil to false" do
+ expect(setting.munge(nil)).to eq false
+ end
+
+ it "munges string 'true' to boolean true" do
+ expect(setting.munge('true')).to eq true
+ end
+
+ it "munges string 'false' to boolean false" do
+ expect(setting.munge('false')).to eq false
+ end
+
+ it "passes absolute paths through" do
+ path = File.expand_path('/path/to/autosign.conf')
+ expect(setting.munge(path)).to eq path
+ end
+
+ it "fails if given anything else" do
+ cases = [1.0, 'sometimes', 'relative/autosign.conf']
+
+ cases.each do |invalid|
+ expect {
+ setting.munge(invalid)
+ }.to raise_error Puppet::Settings::ValidationError, /Invalid autosign value/
+ end
+ end
+ end
+end
diff --git a/spec/unit/settings/file_setting_spec.rb b/spec/unit/settings/file_setting_spec.rb
index a6e3f3f12..d9c6f5629 100755
--- a/spec/unit/settings/file_setting_spec.rb
+++ b/spec/unit/settings/file_setting_spec.rb
@@ -139,14 +139,14 @@ describe Puppet::Settings::FileSetting do
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
+ Puppet::FileSystem::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
+ Puppet::FileSystem::File.expects(:exist?).with(@basepath).returns true
@file.to_resource.should be_instance_of(Puppet::Resource)
end
diff --git a/spec/unit/settings/path_setting_spec.rb b/spec/unit/settings/path_setting_spec.rb
index d2ec46b5d..b7bc3198c 100755
--- a/spec/unit/settings/path_setting_spec.rb
+++ b/spec/unit/settings/path_setting_spec.rb
@@ -22,8 +22,8 @@ describe Puppet::Settings::PathSetting do
end
it "should work with UNC paths" do
- subject.munge('//server/some/path').should == '//server/some/path'
- subject.munge('\\\\server\some\path').should == '//server/some/path'
+ subject.munge('//localhost/some/path').should == '//localhost/some/path'
+ subject.munge('\\\\localhost\some\path').should == '//localhost/some/path'
end
end
end
diff --git a/spec/unit/settings/priority_setting_spec.rb b/spec/unit/settings/priority_setting_spec.rb
new file mode 100755
index 000000000..d51e39dc4
--- /dev/null
+++ b/spec/unit/settings/priority_setting_spec.rb
@@ -0,0 +1,66 @@
+#!/usr/bin/env ruby
+require 'spec_helper'
+
+require 'puppet/settings'
+require 'puppet/settings/priority_setting'
+require 'puppet/util/platform'
+
+describe Puppet::Settings::PrioritySetting do
+ let(:setting) { described_class.new(:settings => mock('settings'), :desc => "test") }
+
+ it "is of type :priority" do
+ setting.type.should == :priority
+ end
+
+ describe "when munging the setting" do
+ it "passes nil through" do
+ setting.munge(nil).should be_nil
+ end
+
+ it "returns the same value if given an integer" do
+ setting.munge(5).should == 5
+ end
+
+ it "returns an integer if given a decimal string" do
+ setting.munge('12').should == 12
+ end
+
+ it "returns a negative integer if given a negative integer string" do
+ setting.munge('-5').should == -5
+ end
+
+ it "fails if given anything else" do
+ [ 'foo', 'realtime', true, 8.3, [] ].each do |value|
+ expect {
+ setting.munge(value)
+ }.to raise_error(Puppet::Settings::ValidationError)
+ end
+ end
+
+ describe "on a Unix-like platform it", :unless => Puppet::Util::Platform.windows? do
+ it "parses high, normal, low, and idle priorities" do
+ {
+ 'high' => -10,
+ 'normal' => 0,
+ 'low' => 10,
+ 'idle' => 19
+ }.each do |value, converted_value|
+ setting.munge(value).should == converted_value
+ end
+ end
+ end
+
+ describe "on a Windows-like platform it", :if => Puppet::Util::Platform.windows? do
+ it "parses high, normal, low, and idle priorities" do
+ {
+ 'high' => Process::HIGH_PRIORITY_CLASS,
+ 'normal' => Process::NORMAL_PRIORITY_CLASS,
+ 'low' => Process::BELOW_NORMAL_PRIORITY_CLASS,
+ 'idle' => Process::IDLE_PRIORITY_CLASS
+ }.each do |value, converted_value|
+ setting.munge(value).should == converted_value
+ end
+ end
+ end
+ end
+end
diff --git a/spec/unit/settings_spec.rb b/spec/unit/settings_spec.rb
index 12107bcb7..b3dc31f3d 100755
--- a/spec/unit/settings_spec.rb
+++ b/spec/unit/settings_spec.rb
@@ -485,7 +485,7 @@ describe Puppet::Settings do
:three => { :default => "$one $two THREE", :desc => "c"},
:four => { :default => "$two $three FOUR", :desc => "d"},
:five => { :default => nil, :desc => "e" }
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
end
describe "call_on_define" do
@@ -589,7 +589,7 @@ describe Puppet::Settings do
:config => { :type => :file, :default => "/my/file", :desc => "a" },
:one => { :default => "ONE", :desc => "a" },
:two => { :default => "TWO", :desc => "b" }
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
@settings.preferred_run_mode = :agent
end
@@ -666,8 +666,8 @@ describe Puppet::Settings do
describe "when root" do
it "should look for the main config file default location config settings haven't been overridden'" do
Puppet.features.stubs(:root?).returns(true)
- FileTest.expects(:exist?).with(main_config_file_default_location).returns(false)
- FileTest.expects(:exist?).with(user_config_file_default_location).never
+ Puppet::FileSystem::File.expects(:exist?).with(main_config_file_default_location).returns(false)
+ Puppet::FileSystem::File.expects(:exist?).with(user_config_file_default_location).never
@settings.send(:parse_config_files)
end
@@ -678,7 +678,7 @@ describe Puppet::Settings do
Puppet.features.stubs(:root?).returns(false)
seq = sequence "load config files"
- FileTest.expects(:exist?).with(user_config_file_default_location).returns(false).in_sequence(seq)
+ Puppet::FileSystem::File.expects(:exist?).with(user_config_file_default_location).returns(false).in_sequence(seq)
@settings.send(:parse_config_files)
end
@@ -699,8 +699,8 @@ describe Puppet::Settings do
:two => { :default => "$one TWO", :desc => "b" },
:three => { :default => "$one $two THREE", :desc => "c" }
@settings.stubs(:user_config_file).returns(@userconfig)
- FileTest.stubs(:exist?).with(@file).returns true
- FileTest.stubs(:exist?).with(@userconfig).returns false
+ Puppet::FileSystem::File.stubs(:exist?).with(@file).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(@userconfig).returns false
end
it "should not ignore the report setting" do
@@ -712,7 +712,7 @@ describe Puppet::Settings do
[puppetd]
report=true
CONF
- FileTest.expects(:exist?).with(myfile).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(myfile).returns(true)
@settings.expects(:read_file).returns(text)
@settings.send(:parse_config_files)
@settings[:report].should be_true
@@ -722,7 +722,7 @@ describe Puppet::Settings do
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
- FileTest.expects(:exist?).with(myfile).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(myfile).returns(true)
File.expects(:read).with(myfile).returns "[main]"
@@ -730,7 +730,7 @@ describe Puppet::Settings do
end
it "should not try to parse non-existent files" do
- FileTest.expects(:exist?).with(@file).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(@file).returns false
File.expects(:read).with(@file).never
@@ -881,6 +881,33 @@ describe Puppet::Settings do
values.should == ["yay/setval"]
end
+ it "should allow hooks invoked at parse time to be deferred" do
+ hook_invoked = false
+ @settings.define_settings :section, :deferred => {:desc => '',
+ :hook => proc { |v| hook_invoked = true },
+ :call_hook => :on_initialize_and_write, }
+
+ @settings.define_settings(:main,
+ :logdir => { :type => :directory, :default => nil, :desc => "logdir" },
+ :confdir => { :type => :directory, :default => nil, :desc => "confdir" },
+ :vardir => { :type => :directory, :default => nil, :desc => "vardir" })
+
+ text = <<-EOD
+ [main]
+ deferred=$confdir/goose
+ EOD
+
+ @settings.stubs(:read_file).returns(text)
+ @settings.initialize_global_settings
+
+ hook_invoked.should be_false
+
+ @settings.initialize_app_defaults(:logdir => '/path/to/logdir', :confdir => '/path/to/confdir', :vardir => '/path/to/vardir')
+
+ hook_invoked.should be_true
+ @settings[:deferred].should eq File.expand_path('/path/to/confdir/goose')
+ end
+
it "should allow empty values" do
@settings.define_settings :section, :myarg => { :default => "myfile", :desc => "a" }
@@ -925,7 +952,7 @@ describe Puppet::Settings do
context "running non-root without explicit config file" do
before :each do
Puppet.features.stubs(:root?).returns(false)
- FileTest.expects(:exist?).
+ Puppet::FileSystem::File.expects(:exist?).
with(user_config_file_default_location).
returns(true).in_sequence(seq)
@settings.expects(:read_file).
@@ -947,7 +974,7 @@ describe Puppet::Settings do
context "running as root without explicit config file" do
before :each do
Puppet.features.stubs(:root?).returns(true)
- FileTest.expects(:exist?).
+ Puppet::FileSystem::File.expects(:exist?).
with(main_config_file_default_location).
returns(true).in_sequence(seq)
@settings.expects(:read_file).
@@ -970,7 +997,7 @@ describe Puppet::Settings do
before :each do
Puppet.features.stubs(:root?).returns(false)
@settings[:confdir] = File.dirname(main_config_file_default_location)
- FileTest.expects(:exist?).
+ Puppet::FileSystem::File.expects(:exist?).
with(main_config_file_default_location).
returns(true).in_sequence(seq)
@settings.expects(:read_file).
@@ -1000,13 +1027,13 @@ describe Puppet::Settings do
:one => { :default => "ONE", :desc => "a" },
:two => { :default => "$one TWO", :desc => "b" },
:three => { :default => "$one $two THREE", :desc => "c" }
- FileTest.stubs(:exist?).with(@file).returns true
- FileTest.stubs(:exist?).with(@userconfig).returns false
+ Puppet::FileSystem::File.stubs(:exist?).with(@file).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(@userconfig).returns false
@settings.stubs(:user_config_file).returns(@userconfig)
end
it "does not create the WatchedFile instance and should not parse if the file does not exist" do
- FileTest.expects(:exist?).with(@file).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(@file).returns false
Puppet::Util::WatchedFile.expects(:new).never
@settings.expects(:parse_config_files).never
@@ -1285,10 +1312,6 @@ describe Puppet::Settings do
@settings.define_settings :files, :myfile => {:type => :file, :default => make_absolute("/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(make_absolute("/otherdir"), 0755)
@@ -1567,17 +1590,6 @@ describe Puppet::Settings do
end
end
- describe "#writesub" do
- it "should only pass valid arguments to File.open" do
- settings = Puppet::Settings.new
- settings.stubs(:get_config_file_default).with(:privatekeydir).returns(OpenStruct.new(:mode => "750"))
-
- File.expects(:open).with("/path/to/keydir", "w", 750).returns true
- settings.writesub(:privatekeydir, "/path/to/keydir")
- end
- end
-
-
describe "when dealing with command-line options" do
let(:settings) { Puppet::Settings.new }
diff --git a/spec/unit/ssl/certificate_authority/autosign_command_spec.rb b/spec/unit/ssl/certificate_authority/autosign_command_spec.rb
new file mode 100644
index 000000000..5792bb4f5
--- /dev/null
+++ b/spec/unit/ssl/certificate_authority/autosign_command_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+require 'puppet/ssl/certificate_authority/autosign_command'
+
+describe Puppet::SSL::CertificateAuthority::AutosignCommand do
+
+ let(:csr) { stub 'csr', :name => 'host', :to_s => 'CSR PEM goes here' }
+ let(:decider) { Puppet::SSL::CertificateAuthority::AutosignCommand.new('/autosign/command') }
+
+ it "returns true if the command succeeded" do
+ executes_the_command_resulting_in(0)
+
+ decider.allowed?(csr).should == true
+ end
+
+ it "returns false if the command failed" do
+ executes_the_command_resulting_in(1)
+
+ decider.allowed?(csr).should == false
+ end
+
+ def executes_the_command_resulting_in(exitstatus)
+ Puppet::Util::Execution.expects(:execute).
+ with(['/autosign/command', 'host'],
+ has_entries(:stdinfile => anything,
+ :combine => true,
+ :failonfail => false)).
+ returns(Puppet::Util::Execution::ProcessOutput.new('', exitstatus))
+ end
+end
diff --git a/spec/unit/ssl/certificate_authority_spec.rb b/spec/unit/ssl/certificate_authority_spec.rb
index 70186a137..13c169a0a 100755
--- a/spec/unit/ssl/certificate_authority_spec.rb
+++ b/spec/unit/ssl/certificate_authority_spec.rb
@@ -92,12 +92,6 @@ describe Puppet::SSL::CertificateAuthority do
Puppet::SSL::CertificateAuthority.new
end
- it "should create an inventory instance" do
- Puppet::SSL::Inventory.expects(:new).returns "inventory"
-
- Puppet::SSL::CertificateAuthority.new.inventory.should == "inventory"
- end
-
it "should make sure the CA is set up" do
Puppet::SSL::CertificateAuthority.any_instance.expects(:setup)
@@ -171,16 +165,16 @@ describe Puppet::SSL::CertificateAuthority do
it "should create and store a password at :capass" do
Puppet[:capass] = File.expand_path("/path/to/pass")
- FileTest.expects(:exist?).with(Puppet[:capass]).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(Puppet[:capass]).returns false
- fh = mock 'filehandle'
- Puppet.settings.expects(:write).with(:capass).yields fh
-
- fh.expects(:print).with { |s| s.length > 18 }
+ fh = StringIO.new
+ Puppet.settings.setting(:capass).expects(:open).with('w').yields fh
@ca.stubs(:sign)
@ca.generate_ca_certificate
+
+ expect(fh.string.length).to be > 18
end
it "should generate a key if one does not exist" do
@@ -238,11 +232,10 @@ describe Puppet::SSL::CertificateAuthority do
Puppet::SSL::Certificate.stubs(:new).returns @cert
- @cert.stubs(:content=)
Puppet::SSL::Certificate.indirection.stubs(:save)
# Stub out the factory
- Puppet::SSL::CertificateFactory.stubs(:build).returns "my real cert"
+ Puppet::SSL::CertificateFactory.stubs(:build).returns @cert.content
@request_content = stub "request content stub", :subject => OpenSSL::X509::Name.new([['CN', @name]]), :public_key => stub('public_key')
@request = stub 'request', :name => @name, :request_extensions => [], :subject_alt_names => [], :content => @request_content
@@ -255,39 +248,6 @@ describe Puppet::SSL::CertificateAuthority do
Puppet::SSL::CertificateRequest.indirection.stubs(:destroy)
end
- describe "and calculating the next certificate serial number" do
- before do
- @path = File.expand_path("/path/to/serial")
- Puppet[:serial] = @path
-
- @filehandle = stub 'filehandle', :<< => @filehandle
- Puppet.settings.stubs(:readwritelock).with(:serial).yields @filehandle
- end
-
- it "should default to 0x1 for the first serial number" do
- @ca.next_serial.should == 0x1
- end
-
- it "should return the current content of the serial file" do
- FileTest.stubs(:exist?).with(@path).returns true
- File.expects(:read).with(@path).returns "0002"
-
- @ca.next_serial.should == 2
- end
-
- it "should write the next serial number to the serial file as hex" do
- @filehandle.expects(:<<).with("0002")
-
- @ca.next_serial
- end
-
- it "should lock the serial file while writing" do
- Puppet.settings.expects(:readwritelock).with(:serial)
-
- @ca.next_serial
- end
- end
-
describe "its own certificate" do
before do
@serial = 10
@@ -303,28 +263,28 @@ describe Puppet::SSL::CertificateAuthority do
it "should use a certificate type of :ca" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
args[0].should == :ca
- end.returns "my real cert"
+ end.returns @cert.content
@ca.sign(@name, :ca, @request)
end
it "should pass the provided CSR as the CSR" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
args[1].should == @request
- end.returns "my real cert"
+ end.returns @cert.content
@ca.sign(@name, :ca, @request)
end
it "should use the provided CSR's content as the issuer" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
args[2].subject.to_s.should == "/CN=myhost"
- end.returns "my real cert"
+ end.returns @cert.content
@ca.sign(@name, :ca, @request)
end
it "should pass the next serial as the serial number" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
args[3].should == @serial
- end.returns "my real cert"
+ end.returns @cert.content
@ca.sign(@name, :ca, @request)
end
@@ -355,7 +315,7 @@ describe Puppet::SSL::CertificateAuthority do
it "should use a certificate type of :server" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
args[0] == :server
- end.returns "my real cert"
+ end.returns @cert.content
@ca.sign(@name)
end
@@ -404,14 +364,14 @@ describe Puppet::SSL::CertificateAuthority do
it "should use the CA certificate as the issuer" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
args[2] == @cacert.content
- end.returns "my real cert"
- @ca.sign(@name)
+ end.returns @cert.content
+ signed = @ca.sign(@name)
end
it "should pass the next serial as the serial number" do
Puppet::SSL::CertificateFactory.expects(:build).with do |*args|
args[3] == @serial
- end.returns "my real cert"
+ end.returns @cert.content
@ca.sign(@name)
end
@@ -518,6 +478,40 @@ describe Puppet::SSL::CertificateAuthority do
end
end
+ it "accepts numeric OIDs under the ppRegCertExt subtree" do
+ exts = [{ 'oid' => '1.3.6.1.4.1.34380.1.1.1',
+ 'value' => '657e4780-4cf5-11e3-8f96-0800200c9a66'}]
+
+ @request.stubs(:request_extensions).returns exts
+
+ expect {
+ @ca.check_internal_signing_policies(@name, @request, false)
+ }.to_not raise_error
+ end
+
+ it "accepts short name OIDs under the ppRegCertExt subtree" do
+ exts = [{ 'oid' => 'pp_uuid',
+ 'value' => '657e4780-4cf5-11e3-8f96-0800200c9a66'}]
+
+ @request.stubs(:request_extensions).returns exts
+
+ expect {
+ @ca.check_internal_signing_policies(@name, @request, false)
+ }.to_not raise_error
+ end
+
+ it "accepts OIDs under the ppPrivCertAttrs subtree" do
+ exts = [{ 'oid' => '1.3.6.1.4.1.34380.1.2.1',
+ 'value' => 'private extension'}]
+
+ @request.stubs(:request_extensions).returns exts
+
+ expect {
+ @ca.check_internal_signing_policies(@name, @request, false)
+ }.to_not raise_error
+ end
+
+
it "should reject a critical extension that isn't on the whitelist" do
@request.stubs(:request_extensions).returns [{ "oid" => "banana",
"value" => "yumm",
@@ -610,76 +604,104 @@ describe Puppet::SSL::CertificateAuthority do
end
describe "when autosigning certificates" do
- let(:autosign) { File.expand_path("/auto/sign") }
- it "should do nothing if autosign is disabled" do
- Puppet[:autosign] = 'false'
+ let(:csr) { Puppet::SSL::CertificateRequest.new("host") }
- Puppet::SSL::CertificateRequest.indirection.expects(:search).never
- @ca.autosign
- end
+ describe "using the autosign setting" do
+ let(:autosign) { File.expand_path("/auto/sign") }
- it "should do nothing if no autosign.conf exists" do
- Puppet[:autosign] = autosign
- FileTest.expects(:exist?).with(autosign).returns false
+ it "should do nothing if autosign is disabled" do
+ Puppet[:autosign] = false
- Puppet::SSL::CertificateRequest.indirection.expects(:search).never
- @ca.autosign
- end
+ @ca.expects(:sign).never
+ @ca.autosign(csr)
+ end
- describe "and autosign is enabled and the autosign.conf file exists" do
- before do
+ it "should do nothing if no autosign.conf exists" do
Puppet[:autosign] = autosign
- FileTest.stubs(:exist?).with(autosign).returns true
- File.stubs(:readlines).with(autosign).returns ["one\n", "two\n"]
+ non_existent_file = Puppet::FileSystem::MemoryFile.a_missing_file(autosign)
+ Puppet::FileSystem::File.overlay(non_existent_file) do
+ @ca.expects(:sign).never
+ @ca.autosign(csr)
+ end
+ end
- Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns []
+ describe "and autosign is enabled and the autosign.conf file exists" do
+ let(:store) { stub 'store', :allow => nil, :allowed? => false }
- @store = stub 'store', :allow => nil
- Puppet::Network::AuthStore.stubs(:new).returns @store
- end
+ before do
+ Puppet[:autosign] = autosign
+ end
- describe "when creating the AuthStore instance to verify autosigning" do
- it "should create an AuthStore with each line in the configuration file allowed to be autosigned" do
- Puppet::Network::AuthStore.expects(:new).returns @store
+ describe "when creating the AuthStore instance to verify autosigning" do
+ it "should create an AuthStore with each line in the configuration file allowed to be autosigned" do
+ Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\ntwo\n")) do
+ Puppet::Network::AuthStore.stubs(:new).returns store
- @store.expects(:allow).with("one")
- @store.expects(:allow).with("two")
+ store.expects(:allow).with("one")
+ store.expects(:allow).with("two")
- @ca.autosign
- end
+ @ca.autosign(csr)
+ end
+ end
- it "should reparse the autosign configuration on each call" do
- Puppet::Network::AuthStore.expects(:new).times(2).returns @store
+ it "should reparse the autosign configuration on each call" do
+ Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one")) do
+ Puppet::Network::AuthStore.stubs(:new).times(2).returns store
- @ca.autosign
- @ca.autosign
- end
+ @ca.autosign(csr)
+ @ca.autosign(csr)
+ end
+ end
- it "should ignore comments" do
- File.stubs(:readlines).with(autosign).returns ["one\n", "#two\n"]
+ it "should ignore comments" do
+ Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\n#two\n")) do
+ Puppet::Network::AuthStore.stubs(:new).returns store
- @store.expects(:allow).with("one")
- @ca.autosign
- end
+ store.expects(:allow).with("one")
- it "should ignore blank lines" do
- File.stubs(:readlines).with(autosign).returns ["one\n", "\n"]
+ @ca.autosign(csr)
+ end
+ end
- @store.expects(:allow).with("one")
- @ca.autosign
+ it "should ignore blank lines" do
+ Puppet::FileSystem::File.overlay(Puppet::FileSystem::MemoryFile.a_regular_file_containing(autosign, "one\n\n")) do
+ Puppet::Network::AuthStore.stubs(:new).returns store
+
+ store.expects(:allow).with("one")
+ @ca.autosign(csr)
+ end
+ end
end
end
+ end
+
+ describe "using the autosign command setting" do
+ let(:cmd) { File.expand_path('/autosign_cmd') }
+ let(:autosign_cmd) { mock 'autosign_command' }
+ let(:autosign_executable) { Puppet::FileSystem::MemoryFile.an_executable(cmd) }
+
+ before do
+ Puppet[:autosign] = cmd
+
+ Puppet::SSL::CertificateAuthority::AutosignCommand.stubs(:new).returns autosign_cmd
+ end
- it "should sign all CSRs whose hostname matches the autosign configuration" do
- csr1 = mock 'csr1'
- csr2 = mock 'csr2'
- Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [csr1, csr2]
+ it "autosigns the CSR if the autosign command returned true" do
+ Puppet::FileSystem::File.overlay(autosign_executable) do
+ autosign_cmd.expects(:allowed?).with(csr).returns true
+
+ @ca.expects(:sign).with('host')
+ @ca.autosign(csr)
+ end
end
- it "should not sign CSRs whose hostname does not match the autosign configuration" do
- csr1 = mock 'csr1'
- csr2 = mock 'csr2'
- Puppet::SSL::CertificateRequest.indirection.stubs(:search).returns [csr1, csr2]
+ it "doesn't autosign the CSR if the autosign_command returned false" do
+ Puppet::FileSystem::File.overlay(autosign_executable) do
+ autosign_cmd.expects(:allowed?).with(csr).returns false
+
+ @ca.expects(:sign).never
+ @ca.autosign(csr)
+ end
end
end
end
@@ -701,28 +723,6 @@ describe Puppet::SSL::CertificateAuthority do
@ca = Puppet::SSL::CertificateAuthority.new
end
- it "should have a method for acting on the SSL files" do
- @ca.should respond_to(:apply)
- end
-
- describe "when applying a method to a set of hosts" do
- it "should fail if no subjects have been specified" do
- expect { @ca.apply(:generate) }.to raise_error(ArgumentError)
- end
-
- it "should create an Interface instance with the specified method and the options" do
- Puppet::SSL::CertificateAuthority::Interface.expects(:new).with(:generate, :to => :host).returns(stub('applier', :apply => nil))
- @ca.apply(:generate, :to => :host)
- end
-
- it "should apply the Interface with itself as the argument" do
- applier = stub('applier')
- applier.expects(:apply).with(@ca)
- Puppet::SSL::CertificateAuthority::Interface.expects(:new).returns applier
- @ca.apply(:generate, :to => :ca_testing)
- end
- end
-
it "should be able to list waiting certificate requests" do
req1 = stub 'req1', :name => "one"
req2 = stub 'req2', :name => "two"
@@ -979,16 +979,15 @@ require 'puppet/indirector/memory'
describe "CertificateAuthority.generate" do
def expect_to_increment_serial_file
- Puppet.settings.expects(:readwritelock).with(:serial)
+ Puppet.settings.setting(:serial).expects(:exclusive_open)
end
def expect_to_sign_a_cert
expect_to_increment_serial_file
- Puppet.settings.expects(:write).with(:cert_inventory, "a")
end
def expect_to_write_the_ca_password
- Puppet.settings.expects(:write).with(:capass)
+ Puppet.settings.setting(:capass).expects(:open).with('w')
end
def expect_ca_initialization
@@ -996,10 +995,6 @@ describe "CertificateAuthority.generate" do
expect_to_sign_a_cert
end
- def avoid_rebuilding_inventory_for_every_cert
- Puppet::SSL::Inventory.any_instance.stubs(:rebuild)
- end
-
INDIRECTED_CLASSES = [
Puppet::SSL::Certificate,
Puppet::SSL::CertificateRequest,
@@ -1021,7 +1016,7 @@ describe "CertificateAuthority.generate" do
end
before do
- avoid_rebuilding_inventory_for_every_cert
+ Puppet::SSL::Inventory.stubs(:new).returns(stub("Inventory", :add => nil))
INDIRECTED_CLASSES.each { |const| const.indirection.terminus_class = :memory }
end
@@ -1036,7 +1031,7 @@ describe "CertificateAuthority.generate" do
let(:ca) { Puppet::SSL::CertificateAuthority.new }
before do
- expect_ca_initialization
+ expect_ca_initialization
end
it "should fail if a certificate already exists for the host" do
diff --git a/spec/unit/ssl/certificate_factory_spec.rb b/spec/unit/ssl/certificate_factory_spec.rb
index 85609593a..fa436edcf 100755
--- a/spec/unit/ssl/certificate_factory_spec.rb
+++ b/spec/unit/ssl/certificate_factory_spec.rb
@@ -115,6 +115,24 @@ describe Puppet::SSL::CertificateFactory do
end
end
+ it "can add custom extension requests" do
+ csr = Puppet::SSL::CertificateRequest.new(name)
+ csr.generate(key)
+
+ csr.stubs(:request_extensions).returns([
+ {'oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'some-value'},
+ {'oid' => 'pp_uuid', 'value' => 'some-uuid'},
+ ])
+
+ cert = subject.build(:client, csr, issuer, serial)
+
+ priv_ext = cert.extensions.find {|ext| ext.oid == '1.3.6.1.4.1.34380.1.2.1'}
+ uuid_ext = cert.extensions.find {|ext| ext.oid == 'pp_uuid'}
+
+ expect(priv_ext.value).to eq 'some-value'
+ expect(uuid_ext.value).to eq 'some-uuid'
+ end
+
# Can't check the CA here, since that requires way more infrastructure
# that I want to build up at this time. We can verify the critical
# values, though, which are non-CA certs. --daniel 2011-10-11
diff --git a/spec/unit/ssl/certificate_request_attributes_spec.rb b/spec/unit/ssl/certificate_request_attributes_spec.rb
new file mode 100644
index 000000000..6165330aa
--- /dev/null
+++ b/spec/unit/ssl/certificate_request_attributes_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+require 'puppet/ssl/certificate_request_attributes'
+
+describe Puppet::SSL::CertificateRequestAttributes do
+
+ let(:expected) do
+ {
+ "custom_attributes" => {
+ "1.3.6.1.4.1.34380.2.2"=>[3232235521, 3232235777], # system IPs in hex
+ "1.3.6.1.4.1.34380.2.0"=>"hostname.domain.com",
+ }
+ }
+ end
+ let(:csr_attributes_hash) { expected.dup }
+ let(:csr_attributes_path) { '/some/where/csr_attributes.yaml' }
+ let(:csr_attributes) { Puppet::SSL::CertificateRequestAttributes.new(csr_attributes_path) }
+
+ it "initializes with a path" do
+ expect(csr_attributes.path).to eq(csr_attributes_path)
+ end
+
+ describe "loading" do
+ it "returns nil when loading from a non-existent file" do
+ expect(csr_attributes.load).to be_false
+ end
+
+ context "with an available attributes file" do
+ before do
+ Puppet::FileSystem::File.expects(:exist?).with(csr_attributes_path).returns(true)
+ Puppet::Util::Yaml.expects(:load_file).with(csr_attributes_path, {}).returns(csr_attributes_hash)
+ end
+
+ it "loads csr attributes from a file when the file is present" do
+ expect(csr_attributes.load).to be_true
+ end
+
+ it "exposes custom_attributes" do
+ csr_attributes.load
+ expect(csr_attributes.custom_attributes).to eq(expected['custom_attributes'])
+ end
+
+ it "returns an empty hash if custom_attributes points to nil" do
+ csr_attributes_hash["custom_attributes"] = nil
+ csr_attributes.load
+ expect(csr_attributes.custom_attributes).to eq({})
+ end
+
+ it "returns an empty hash if custom_attributes key is not present" do
+ csr_attributes_hash.delete("custom_attributes")
+ csr_attributes.load
+ expect(csr_attributes.custom_attributes).to eq({})
+ end
+
+ it "raise a Puppet::Error if an unexpected root key is defined" do
+ csr_attributes_hash['unintentional'] = 'data'
+ expect { csr_attributes.load }.to raise_error(Puppet::Error, /unexpected attributes.*unintentional/)
+ end
+ end
+ end
+end
diff --git a/spec/unit/ssl/certificate_request_spec.rb b/spec/unit/ssl/certificate_request_spec.rb
index 1dc449d53..192c929b5 100755
--- a/spec/unit/ssl/certificate_request_spec.rb
+++ b/spec/unit/ssl/certificate_request_spec.rb
@@ -178,6 +178,109 @@ describe Puppet::SSL::CertificateRequest do
end
end
+ context "with custom CSR attributes" do
+
+ it "adds attributes with single values" do
+ csr_attributes = {
+ '1.3.6.1.4.1.34380.1.2.1' => 'CSR specific info',
+ '1.3.6.1.4.1.34380.1.2.2' => 'more CSR specific info',
+ }
+
+ request.generate(key, :csr_attributes => csr_attributes)
+
+ attrs = request.custom_attributes
+ attrs.should include({'oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'CSR specific info'})
+ attrs.should include({'oid' => '1.3.6.1.4.1.34380.1.2.2', 'value' => 'more CSR specific info'})
+ end
+
+ ['extReq', '1.2.840.113549.1.9.14'].each do |oid|
+ it "doesn't overwrite standard PKCS#9 CSR attribute '#{oid}'" do
+ expect do
+ request.generate(key, :csr_attributes => {oid => 'data'})
+ end.to raise_error ArgumentError, /Cannot specify.*#{oid}/
+ end
+ end
+
+ ['msExtReq', '1.3.6.1.4.1.311.2.1.14'].each do |oid|
+ it "doesn't overwrite Microsoft extension request OID '#{oid}'" do
+ expect do
+ request.generate(key, :csr_attributes => {oid => 'data'})
+ end.to raise_error ArgumentError, /Cannot specify.*#{oid}/
+ end
+ end
+
+ it "raises an error if an attribute cannot be created" do
+ csr_attributes = { "thats.no.moon" => "death star" }
+
+ expect do
+ request.generate(key, :csr_attributes => csr_attributes)
+ end.to raise_error Puppet::Error, /Cannot create CSR with attribute thats\.no\.moon: first num too large/
+ end
+ end
+
+ context "with extension requests" do
+ let(:extension_data) do
+ {
+ '1.3.6.1.4.1.34380.1.1.31415' => 'pi',
+ '1.3.6.1.4.1.34380.1.1.2718' => 'e',
+ }
+ end
+
+ it "adds an extreq attribute to the CSR" do
+ request.generate(key, :extension_requests => extension_data)
+
+ exts = request.content.attributes.select { |attr| attr.oid = 'extReq' }
+ exts.length.should == 1
+ end
+
+ it "adds an extension for each entry in the extension request structure" do
+ request.generate(key, :extension_requests => extension_data)
+
+ exts = request.request_extensions
+
+ exts.should include('oid' => '1.3.6.1.4.1.34380.1.1.31415', 'value' => 'pi')
+ exts.should include('oid' => '1.3.6.1.4.1.34380.1.1.2718', 'value' => 'e')
+ end
+
+ it "defines the extensions as non-critical" do
+ request.generate(key, :extension_requests => extension_data)
+ request.request_extensions.each do |ext|
+ ext['critical'].should be_false
+ end
+ end
+
+ it "rejects the subjectAltNames extension" do
+ san_names = ['subjectAltName', '2.5.29.17']
+ san_field = 'DNS:first.tld, DNS:second.tld'
+
+ san_names.each do |name|
+ expect do
+ request.generate(key, :extension_requests => {name => san_field})
+ end.to raise_error Puppet::Error, /conflicts with internally used extension/
+ end
+ end
+
+ it "merges the extReq attribute with the subjectAltNames extension" do
+ request.generate(key,
+ :dns_alt_names => 'first.tld, second.tld',
+ :extension_requests => extension_data)
+ exts = request.request_extensions
+
+ exts.should include('oid' => '1.3.6.1.4.1.34380.1.1.31415', 'value' => 'pi')
+ exts.should include('oid' => '1.3.6.1.4.1.34380.1.1.2718', 'value' => 'e')
+ exts.should include('oid' => 'subjectAltName', 'value' => 'DNS:first.tld, DNS:myname, DNS:second.tld')
+
+ request.subject_alt_names.should eq ['DNS:first.tld', 'DNS:myname', 'DNS:second.tld']
+ end
+
+ it "raises an error if the OID could not be created" do
+ exts = {"thats.no.moon" => "death star"}
+ expect do
+ request.generate(key, :extension_requests => exts)
+ end.to raise_error Puppet::Error, /Cannot create CSR with extension request thats\.no\.moon: first num too large/
+ end
+ end
+
it "should sign the csr with the provided key" do
request.generate(key)
request.content.verify(key.content.public_key).should be_true
diff --git a/spec/unit/ssl/certificate_spec.rb b/spec/unit/ssl/certificate_spec.rb
index 70d35d4c7..c8f9930f3 100755
--- a/spec/unit/ssl/certificate_spec.rb
+++ b/spec/unit/ssl/certificate_spec.rb
@@ -75,6 +75,17 @@ describe Puppet::SSL::Certificate do
end
describe "when managing instances" do
+
+ def build_cert(opts)
+ key = Puppet::SSL::Key.new('quux')
+ key.generate
+ csr = Puppet::SSL::CertificateRequest.new('quux')
+ csr.generate(key, opts)
+
+ raw_cert = Puppet::SSL::CertificateFactory.build('client', csr, csr.content, 14)
+ @class.from_instance(raw_cert)
+ end
+
before do
@certificate = @class.new("myname")
end
@@ -93,33 +104,35 @@ describe Puppet::SSL::Certificate do
describe "#subject_alt_names" do
it "should list all alternate names when the extension is present" do
- key = Puppet::SSL::Key.new('quux')
- key.generate
-
- csr = Puppet::SSL::CertificateRequest.new('quux')
- csr.generate(key, :dns_alt_names => 'foo, bar,baz')
-
- raw_csr = csr.content
-
- cert = Puppet::SSL::CertificateFactory.build('server', csr, raw_csr, 14)
- certificate = @class.from_s(cert.to_pem)
+ certificate = build_cert(:dns_alt_names => 'foo, bar,baz')
certificate.subject_alt_names.
should =~ ['DNS:foo', 'DNS:bar', 'DNS:baz', 'DNS:quux']
end
it "should return an empty list of names if the extension is absent" do
- key = Puppet::SSL::Key.new('quux')
- key.generate
+ certificate = build_cert({})
+ certificate.subject_alt_names.should be_empty
+ end
+ end
- csr = Puppet::SSL::CertificateRequest.new('quux')
- csr.generate(key)
+ describe "custom extensions" do
+ it "returns extensions under the ppRegCertExt" do
+ exts = {'pp_uuid' => 'abcdfd'}
+ cert = build_cert(:extension_requests => exts)
+ expect(cert.custom_extensions).to include('oid' => 'pp_uuid', 'value' => 'abcdfd')
+ end
- raw_csr = csr.content
+ it "returns extensions under the ppPrivCertExt" do
+ exts = {'1.3.6.1.4.1.34380.1.2.1' => 'x509 :('}
+ cert = build_cert(:extension_requests => exts)
+ expect(cert.custom_extensions).to include('oid' => '1.3.6.1.4.1.34380.1.2.1', 'value' => 'x509 :(')
+ end
- cert = Puppet::SSL::CertificateFactory.build('client', csr, raw_csr, 14)
- certificate = @class.from_s(cert.to_pem)
- certificate.subject_alt_names.should be_empty
+ it "doesn't return standard extensions" do
+ cert = build_cert(:dns_alt_names => 'foo')
+ expect(cert.custom_extensions).to be_empty
end
+
end
it "should return a nil expiration if there is no actual certificate" do
diff --git a/spec/unit/ssl/host_spec.rb b/spec/unit/ssl/host_spec.rb
index 8240991c7..3b341cc4e 100755
--- a/spec/unit/ssl/host_spec.rb
+++ b/spec/unit/ssl/host_spec.rb
@@ -9,9 +9,24 @@ def base_pson_comparison(result, pson_hash)
result["state"].should == pson_hash["desired_state"]
end
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ HOST_SCHEMA = JSON.parse(File.read(File.join(File.dirname(__FILE__), '../../../api/schemas/host.json')))
+
+ describe "host schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, HOST_SCHEMA)
+ end
+ end
+end
+
describe Puppet::SSL::Host do
include PuppetSpec::Files
+ def validate_json_for_host(host)
+ JSON::Validator.validate!(HOST_SCHEMA, host.to_pson)
+ end
+
before do
Puppet::SSL::Host.indirection.terminus_class = :file
@@ -823,7 +838,7 @@ describe Puppet::SSL::Host do
let(:host) do
Puppet::SSL::Host.new("bazinga")
end
-
+
let(:pson_hash) do
{
"fingerprint" => host.certificate_request.fingerprint,
@@ -831,15 +846,20 @@ describe Puppet::SSL::Host do
"name" => host.name
}
end
-
+
it "should be able to identify a host with an unsigned certificate request" do
host.generate_certificate_request
result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
-
+
base_pson_comparison result, pson_hash
end
-
+
+ it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do
+ host.generate_certificate_request
+ validate_json_for_host(host)
+ end
+
describe "explicit fingerprints" do
[:SHA1, :SHA256, :SHA512].each do |md|
it "should include #{md}" do
@@ -854,7 +874,7 @@ describe Puppet::SSL::Host do
end
end
end
-
+
describe "dns_alt_names" do
describe "when not specified" do
it "should include the dns_alt_names associated with the certificate" do
@@ -867,22 +887,28 @@ describe Puppet::SSL::Host do
end
end
- [ "",
+ [ "",
"test, alt, names"
].each do |alt_names|
describe "when #{alt_names}" do
- it "should include the dns_alt_names associated with the certificate" do
+ before(:each) do
host.generate_certificate_request :dns_alt_names => alt_names
+ end
+
+ it "should include the dns_alt_names associated with the certificate" do
pson_hash["desired_alt_names"] = host.certificate_request.subject_alt_names
result = PSON.parse(Puppet::SSL::Host.new(host.name).to_pson)
base_pson_comparison result, pson_hash
result["dns_alt_names"].should == pson_hash["desired_alt_names"]
end
+
+ it "should validate against the schema", :unless => Puppet.features.microsoft_windows? do
+ validate_json_for_host(host)
+ end
end
end
end
-
it "should be able to identify a host with a signed certificate" do
host.generate_certificate_request
diff --git a/spec/unit/ssl/inventory_spec.rb b/spec/unit/ssl/inventory_spec.rb
index b145cd9ab..9bcbbcea5 100755
--- a/spec/unit/ssl/inventory_spec.rb
+++ b/spec/unit/ssl/inventory_spec.rb
@@ -20,7 +20,7 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
before do
Puppet[:cert_inventory] = cert_inventory
- FileTest.stubs(:exist?).with(cert_inventory).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(cert_inventory).returns true
@inventory = @class.new
@@ -28,86 +28,51 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
end
describe "and creating the inventory file" do
- before do
- Puppet.settings.stubs(:write)
- FileTest.stubs(:exist?).with(cert_inventory).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'
-
+ it "re-adds all of the existing certificates" do
+ inventory_file = StringIO.new
+ Puppet.settings.setting(:cert_inventory).stubs(:open).yields(inventory_file)
+
+ cert1 = Puppet::SSL::Certificate.new("cert1")
+ cert1.content = stub 'cert1',
+ :serial => 2,
+ :not_before => Time.now,
+ :not_after => Time.now,
+ :subject => "/CN=smocking"
+ cert2 = Puppet::SSL::Certificate.new("cert2")
+ cert2.content = stub 'cert2',
+ :serial => 3,
+ :not_before => Time.now,
+ :not_after => Time.now,
+ :subject => "/CN=mocking bird"
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
+
+ expect(inventory_file.string).to match(/\/CN=smocking/)
+ expect(inventory_file.string).to match(/\/CN=mocking bird/)
end
end
describe "and adding a certificate" do
- it "should build the inventory file if one does not exist" do
- Puppet[:cert_inventory] = cert_inventory
- Puppet.settings.stubs(:write)
-
- FileTest.expects(:exist?).with(cert_inventory).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")
+ Puppet.settings.setting(:cert_inventory).expects(:open).with("a")
@inventory.add(@cert)
end
- it "should use the actual certificate if it was passed a Puppet certificate" do
+ it "should add formatted certificate information to the end of the file" 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
+ fh = StringIO.new
+ Puppet.settings.setting(:cert_inventory).expects(:open).with("a").yields(fh)
@inventory.expects(:format).with(@cert).returns "myformat"
- fh.expects(:print).with("myformat")
-
@inventory.add(@cert)
+
+ expect(fh.string).to eq("myformat")
end
end
@@ -152,7 +117,7 @@ describe Puppet::SSL::Inventory, :unless => Puppet.features.microsoft_windows? d
describe "and finding a serial number" do
it "should return nil if the inventory file is missing" do
- FileTest.expects(:exist?).with(cert_inventory).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(cert_inventory).returns false
@inventory.serial(:whatever).should be_nil
end
diff --git a/spec/unit/ssl/key_spec.rb b/spec/unit/ssl/key_spec.rb
index 112505087..4cea5491c 100755
--- a/spec/unit/ssl/key_spec.rb
+++ b/spec/unit/ssl/key_spec.rb
@@ -71,7 +71,7 @@ describe Puppet::SSL::Key do
end
it "should not try to use the provided password file if the file does not exist" do
- FileTest.stubs(:exist?).returns false
+ Puppet::FileSystem::File.stubs(:exist?).returns false
@key.password_file = "/path/to/password"
path = "/my/path"
@@ -84,7 +84,7 @@ describe Puppet::SSL::Key do
end
it "should read the key with the password retrieved from the password file if one is provided" do
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
@key.password_file = "/path/to/password"
path = "/my/path"
@@ -154,7 +154,7 @@ describe Puppet::SSL::Key do
describe "with a password file set" do
it "should return a nil password if the password file does not exist" do
- FileTest.expects(:exist?).with("/path/to/pass").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("/path/to/pass").returns false
File.expects(:read).with("/path/to/pass").never
@instance.password_file = "/path/to/pass"
@@ -163,7 +163,7 @@ describe Puppet::SSL::Key do
end
it "should return the contents of the password file as its password" do
- FileTest.expects(:exist?).with("/path/to/pass").returns true
+ Puppet::FileSystem::File.expects(:exist?).with("/path/to/pass").returns true
File.expects(:read).with("/path/to/pass").returns "my password"
@instance.password_file = "/path/to/pass"
diff --git a/spec/unit/ssl/oids_spec.rb b/spec/unit/ssl/oids_spec.rb
new file mode 100644
index 000000000..ee95bb942
--- /dev/null
+++ b/spec/unit/ssl/oids_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+require 'puppet/ssl/oids'
+
+describe Puppet::SSL::Oids do
+ describe "defining application OIDs" do
+
+ {
+ 'puppetlabs' => '1.3.6.1.4.1.34380',
+ 'ppCertExt' => '1.3.6.1.4.1.34380.1',
+ 'ppRegCertExt' => '1.3.6.1.4.1.34380.1.1',
+ 'pp_uuid' => '1.3.6.1.4.1.34380.1.1.1',
+ 'pp_instance_id' => '1.3.6.1.4.1.34380.1.1.2',
+ 'pp_image_name' => '1.3.6.1.4.1.34380.1.1.3',
+ 'pp_preshared_key' => '1.3.6.1.4.1.34380.1.1.4',
+ 'ppPrivCertExt' => '1.3.6.1.4.1.34380.1.2',
+ }.each_pair do |sn, oid|
+ it "defines #{sn} as #{oid}" do
+ object_id = OpenSSL::ASN1::ObjectId.new(sn)
+ expect(object_id.oid).to eq oid
+ end
+ end
+ end
+
+ describe "checking if an OID is a subtree of another OID" do
+
+ it "can determine if an OID is contained in another OID" do
+ described_class.subtree_of?('1.3.6.1', '1.3.6.1.4.1').should be_true
+ described_class.subtree_of?('1.3.6.1.4.1', '1.3.6.1').should be_false
+ end
+
+ it "returns true if an OID is compared against itself and exclusive is false" do
+ described_class.subtree_of?('1.3.6.1', '1.3.6.1', false).should be_true
+ end
+
+ it "returns false if an OID is compared against itself and exclusive is true" do
+ described_class.subtree_of?('1.3.6.1', '1.3.6.1', true).should be_false
+ end
+
+ it "can compare OIDs defined as short names" do
+ described_class.subtree_of?('IANA', '1.3.6.1.4.1').should be_true
+ described_class.subtree_of?('1.3.6.1', 'enterprises').should be_true
+ end
+
+ it "returns false when an invalid OID shortname is passed" do
+ described_class.subtree_of?('IANA', 'bananas').should be_false
+ end
+ end
+end
diff --git a/spec/unit/ssl/validator_spec.rb b/spec/unit/ssl/validator_spec.rb
index fe0904cb8..2b8cfb0f9 100644
--- a/spec/unit/ssl/validator_spec.rb
+++ b/spec/unit/ssl/validator_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
-require 'puppet/ssl/validator'
+require 'puppet/ssl'
require 'puppet/ssl/configuration'
-describe Puppet::SSL::Validator do
+describe Puppet::SSL::Validator::DefaultValidator do
let(:ssl_context) do
mock('OpenSSL::X509::StoreContext')
end
@@ -14,8 +14,16 @@ describe Puppet::SSL::Validator do
:ca_auth_file => Puppet[:ssl_client_ca_auth])
end
+ let(:ssl_host) do
+ stub('ssl_host',
+ :ssl_store => nil,
+ :certificate => stub('cert', :content => nil),
+ :key => stub('key', :content => nil))
+ end
+
subject do
- described_class.new(:ssl_configuration => ssl_configuration)
+ described_class.new(ssl_configuration,
+ ssl_host)
end
before :each do
@@ -49,17 +57,20 @@ describe Puppet::SSL::Validator do
before :each do
ssl_context.stubs(:error_string).returns("Something went wrong.")
end
+
it 'does not make the error available via #verify_errors' do
subject.call(true, ssl_context)
subject.verify_errors.should == []
end
end
+
context 'and the chain is valid' do
it 'is true for each CA certificate in the chain' do
(cert_chain.length - 1).times do
subject.call(true, ssl_context).should be_true
end
end
+
it 'is true for the SSL certificate ending the chain' do
(cert_chain.length - 1).times do
subject.call(true, ssl_context)
@@ -67,17 +78,20 @@ describe Puppet::SSL::Validator do
subject.call(true, ssl_context).should be_true
end
end
+
context 'and the chain is invalid' do
before :each do
ssl_configuration.stubs(:read_file).
with(Puppet[:localcacert]).
returns(agent_ca)
end
+
it 'is true for each CA certificate in the chain' do
(cert_chain.length - 1).times do
subject.call(true, ssl_context).should be_true
end
end
+
it 'is false for the SSL certificate ending the chain' do
(cert_chain.length - 1).times do
subject.call(true, ssl_context)
@@ -85,13 +99,16 @@ describe Puppet::SSL::Validator do
subject.call(true, ssl_context).should be_false
end
end
+
context 'an error is raised inside of #call' do
before :each do
ssl_context.expects(:current_cert).raises(StandardError, "BOOM!")
end
+
it 'is false' do
subject.call(true, ssl_context).should be_false
end
+
it 'makes the error available through #verify_errors' do
subject.call(true, ssl_context)
subject.verify_errors.should == ["BOOM!"]
@@ -100,11 +117,28 @@ describe Puppet::SSL::Validator do
end
end
- describe '#register_verify_callback' do
- it 'registers itself using #verify_callback' do
+ describe '#setup_connection' do
+ it 'updates the connection for verification' do
+ subject.stubs(:ssl_certificates_are_present?).returns(true)
connection = mock('Net::HTTP')
+
+ connection.expects(:cert_store=).with(ssl_host.ssl_store)
+ connection.expects(:ca_file=).with(ssl_configuration.ca_auth_file)
+ connection.expects(:cert=).with(ssl_host.certificate.content)
+ connection.expects(:key=).with(ssl_host.key.content)
connection.expects(:verify_callback=).with(subject)
- subject.register_verify_callback(connection)
+ connection.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
+
+ subject.setup_connection(connection)
+ end
+
+ it 'does not perform verification if certificate files are missing' do
+ subject.stubs(:ssl_certificates_are_present?).returns(false)
+ connection = mock('Net::HTTP')
+
+ connection.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
+
+ subject.setup_connection(connection)
end
end
@@ -120,17 +154,21 @@ describe Puppet::SSL::Validator do
before :each do
subject.stubs(:has_authz_peer_cert).returns(true)
end
+
it 'is true' do
subject.valid_peer?.should be_true
end
end
+
context 'when the peer presents an invalid chain' do
before :each do
subject.stubs(:has_authz_peer_cert).returns(false)
end
+
it 'is false' do
subject.valid_peer?.should be_false
end
+
it 'makes a helpful error message available via #verify_errors' do
subject.valid_peer?
subject.verify_errors.should == [expected_authz_error_msg]
@@ -143,22 +181,27 @@ describe Puppet::SSL::Validator do
it 'returns true when the SSL cert is issued by the Master CA' do
subject.has_authz_peer_cert(cert_chain, [root_ca_cert]).should be_true
end
+
it 'returns true when the SSL cert is issued by the Agent CA' do
subject.has_authz_peer_cert(cert_chain_agent_ca, [root_ca_cert]).should be_true
end
end
+
context 'when the Master CA is listed as authorized' do
it 'returns false when the SSL cert is issued by the Master CA' do
subject.has_authz_peer_cert(cert_chain, [master_ca_cert]).should be_true
end
+
it 'returns true when the SSL cert is issued by the Agent CA' do
subject.has_authz_peer_cert(cert_chain_agent_ca, [master_ca_cert]).should be_false
end
end
+
context 'when the Agent CA is listed as authorized' do
it 'returns true when the SSL cert is issued by the Master CA' do
subject.has_authz_peer_cert(cert_chain, [agent_ca_cert]).should be_false
end
+
it 'returns true when the SSL cert is issued by the Agent CA' do
subject.has_authz_peer_cert(cert_chain_agent_ca, [agent_ca_cert]).should be_true
end
diff --git a/spec/unit/status_spec.rb b/spec/unit/status_spec.rb
index 3f6ae7d82..8f598f579 100755
--- a/spec/unit/status_spec.rb
+++ b/spec/unit/status_spec.rb
@@ -37,4 +37,13 @@ describe Puppet::Status do
new_status = Puppet::Status.convert_from('yaml', status.render('yaml'))
new_status.should equal_attributes_of(status)
end
+
+ it "serializes to PSON that conforms to the status schema", :unless => Puppet.features.microsoft_windows? do
+ schema = JSON.parse(File.read('api/schemas/status.json'))
+ status = Puppet::Status.new
+ status.version = Puppet.version
+
+ JSON::Validator.validate!(JSON_META_SCHEMA, schema)
+ JSON::Validator.validate!(schema, status.render('pson'))
+ end
end
diff --git a/spec/unit/transaction/event_spec.rb b/spec/unit/transaction/event_spec.rb
index a60e6e907..8e62e02f6 100755
--- a/spec/unit/transaction/event_spec.rb
+++ b/spec/unit/transaction/event_spec.rb
@@ -15,14 +15,6 @@ end
describe Puppet::Transaction::Event do
include PuppetSpec::Files
- [:previous_value, :desired_value, :property, :name, :message, :file, :line, :tags, :audited].each do |attr|
- it "should support #{attr}" do
- event = Puppet::Transaction::Event.new
- event.send(attr.to_s + "=", "foo")
- event.send(attr).should == "foo"
- end
- end
-
it "should support resource" do
event = Puppet::Transaction::Event.new
event.resource = TestResource.new
@@ -101,7 +93,7 @@ describe Puppet::Transaction::Event do
end
it "should set the tags to the event tags" do
- Puppet::Util::Log.expects(:new).with { |args| args[:tags] == %w{one two} }
+ Puppet::Util::Log.expects(:new).with { |args| args[:tags].to_a.should =~ %w{one two} }
Puppet::Transaction::Event.new(:tags => %w{one two}).send_log
end
diff --git a/spec/unit/transaction/report_spec.rb b/spec/unit/transaction/report_spec.rb
index 7195b0623..661349dab 100755
--- a/spec/unit/transaction/report_spec.rb
+++ b/spec/unit/transaction/report_spec.rb
@@ -4,6 +4,19 @@ require 'spec_helper'
require 'puppet'
require 'puppet/transaction/report'
+# the json-schema gem doesn't support windows
+if not Puppet.features.microsoft_windows?
+ REPORT_SCHEMA_URI = File.join(File.dirname(__FILE__), '../../../api/schemas/report.json')
+ REPORT_SCHEMA = JSON.parse(File.read(REPORT_SCHEMA_URI))
+
+ describe "report schema" do
+ it "should validate against the json meta-schema" do
+ JSON::Validator.validate!(JSON_META_SCHEMA, REPORT_SCHEMA)
+ end
+ end
+
+end
+
describe Puppet::Transaction::Report do
include PuppetSpec::Files
before do
@@ -392,6 +405,12 @@ describe Puppet::Transaction::Report do
expect_equivalent_reports(tripped, report)
end
+ it "generates pson which validates against the report schema", :unless => Puppet.features.microsoft_windows? do
+ Puppet[:report_serialization_format] = "pson"
+ report = generate_report
+ JSON::Validator.validate!(REPORT_SCHEMA, report.render)
+ end
+
it "can make a round trip through yaml" do
Puppet[:report_serialization_format] = "yaml"
report = generate_report
@@ -458,7 +477,7 @@ describe Puppet::Transaction::Report do
status = Puppet::Resource::Status.new(Puppet::Type.type(:notify).new(:title => "a resource"))
status.changed = true
- report = Puppet::Transaction::Report.new('testy', 1357986, 'test_environment', "df34516e-4050-402d-a166-05b03b940749")
+ report = Puppet::Transaction::Report.new('apply', 1357986, 'test_environment', "df34516e-4050-402d-a166-05b03b940749")
report << Puppet::Util::Log.new(:level => :warning, :message => "log message")
report.add_times("timing", 4)
report.add_resource_status(status)
diff --git a/spec/unit/transaction/resource_harness_spec.rb b/spec/unit/transaction/resource_harness_spec.rb
index 0c250ae92..afe1ec912 100755
--- a/spec/unit/transaction/resource_harness_spec.rb
+++ b/spec/unit/transaction/resource_harness_spec.rb
@@ -127,6 +127,34 @@ describe Puppet::Transaction::ResourceHarness do
false
end
end
+
+ newproperty(:brillig) do
+ desc "A property that raises a StandardError exception when you test if it's insync?"
+ def sync
+ end
+
+ def retrieve
+ :absent
+ end
+
+ def insync?(reference_value)
+ raise ZeroDivisionError.new('brillig')
+ end
+ end
+
+ newproperty(:slithy) do
+ desc "A property that raises an Exception when you test if it's insync?"
+ def sync
+ end
+
+ def retrieve
+ :absent
+ end
+
+ def insync?(reference_value)
+ raise Exception.new('slithy')
+ end
+ end
end
stubProvider
end
@@ -164,6 +192,35 @@ describe Puppet::Transaction::ResourceHarness do
end
end
+ describe "when a StandardError exception occurs during insync?" do
+ before :each do
+ stub_provider = make_stub_provider
+ @resource = stub_provider.new :name => 'name', :brillig => 1
+ @resource.expects(:err).never
+ end
+
+ it "should record a failure event" do
+ @status = @harness.evaluate(@resource)
+ @status.events[0].name.to_s.should == 'brillig_changed'
+ @status.events[0].property.should == 'brillig'
+ @status.events[0].status.should == 'failure'
+ end
+ end
+
+ describe "when an Exception occurs during insync?" do
+ before :each do
+ stub_provider = make_stub_provider
+ @resource = stub_provider.new :name => 'name', :slithy => 1
+ @resource.expects(:err).never
+ end
+
+ it "should log and pass the exception through" do
+ lambda { @harness.evaluate(@resource) }.should raise_error(Exception, /slithy/)
+ @logs.first.message.should == "change from absent to 1 failed: slithy"
+ @logs.first.level.should == :err
+ end
+ end
+
describe "when auditing" do
it "should not call insync? on parameters that are merely audited" do
stub_provider = make_stub_provider
@@ -180,7 +237,9 @@ describe Puppet::Transaction::ResourceHarness do
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
@@ -188,222 +247,13 @@ describe Puppet::Transaction::ResourceHarness do
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
+ Puppet::FileSystem::File.exist?(test_file).should == false
end
end
diff --git a/spec/unit/transaction_spec.rb b/spec/unit/transaction_spec.rb
index 840b34c15..04ce08c4f 100755
--- a/spec/unit/transaction_spec.rb
+++ b/spec/unit/transaction_spec.rb
@@ -308,7 +308,7 @@ describe Puppet::Transaction do
transaction.evaluate
generated.each do |res|
- res.must be_tagged(generator.tags)
+ res.must be_tagged(*generator.tags)
end
end
end
@@ -404,7 +404,6 @@ describe Puppet::Transaction do
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
@@ -478,6 +477,48 @@ describe Puppet::Transaction do
end
end
+ describe "during teardown" do
+ before :each do
+ @catalog = Puppet::Resource::Catalog.new
+ @transaction = Puppet::Transaction.new(@catalog, nil, Puppet::Graph::RandomPrioritizer.new)
+ end
+
+ it "should call ::post_resource_eval on provider classes that support it" do
+ @resource = Puppet::Type.type(:notify).new :title => "foo"
+ @catalog.add_resource @resource
+
+ # 'expects' will cause 'respond_to?(:post_resource_eval)' to return true
+ @resource.provider.class.expects(:post_resource_eval)
+ @transaction.evaluate
+ end
+
+ it "should call ::post_resource_eval even if other providers' ::post_resource_eval fails" do
+ @resource3 = Puppet::Type.type(:user).new :title => "bloo"
+ @resource3.provider.class.stubs(:post_resource_eval).raises
+ @resource4 = Puppet::Type.type(:notify).new :title => "blob"
+ @resource4.provider.class.stubs(:post_resource_eval).raises
+ @catalog.add_resource @resource3
+ @catalog.add_resource @resource4
+
+ # ruby's Set does not guarantee ordering, so both resource3 and resource4
+ # need to expect post_resource_eval, rather than just the 'first' one.
+ @resource3.provider.class.expects(:post_resource_eval)
+ @resource4.provider.class.expects(:post_resource_eval)
+
+ @transaction.evaluate
+ end
+
+ it "should call ::post_resource_eval even if one of the resources fails" do
+ @resource3 = Puppet::Type.type(:notify).new :title => "bloo"
+ @resource3.stubs(:retrieve_resource).raises
+ @catalog.add_resource @resource3
+
+ @resource3.provider.class.expects(:post_resource_eval)
+
+ @transaction.evaluate
+ end
+ end
+
describe 'when checking application run state' do
before do
@catalog = Puppet::Resource::Catalog.new
@@ -594,32 +635,37 @@ describe Puppet::Transaction, " when determining tags" do
it "should default to the tags specified in the :tags setting" do
Puppet[:tags] = "one"
- @transaction.tags.should == %w{one}
+ @transaction.should be_tagged("one")
end
it "should split tags based on ','" do
Puppet[:tags] = "one,two"
- @transaction.tags.should == %w{one two}
+ @transaction.should be_tagged("one")
+ @transaction.should be_tagged("two")
end
it "should use any tags set after creation" do
Puppet[:tags] = ""
@transaction.tags = %w{one two}
- @transaction.tags.should == %w{one two}
+ @transaction.should be_tagged("one")
+ @transaction.should be_tagged("two")
end
it "should always convert assigned tags to an array" do
@transaction.tags = "one::two"
- @transaction.tags.should == %w{one::two}
+ @transaction.should be_tagged("one::two")
end
it "should accept a comma-delimited string" do
@transaction.tags = "one, two"
- @transaction.tags.should == %w{one two}
+ @transaction.should be_tagged("one")
+ @transaction.should be_tagged("two")
end
it "should accept an empty string" do
+ @transaction.tags = "one, two"
+ @transaction.should be_tagged("one")
@transaction.tags = ""
- @transaction.tags.should == []
+ @transaction.should_not be_tagged("one")
end
end
diff --git a/spec/unit/type/component_spec.rb b/spec/unit/type/component_spec.rb
index 82c822dea..f3326a161 100755
--- a/spec/unit/type/component_spec.rb
+++ b/spec/unit/type/component_spec.rb
@@ -42,8 +42,8 @@ describe component do
component.new(:name => "Class[foo]").pathbuilder.must == ["Foo"]
end
- it "should produce an empty string if the component models the 'main' class" do
- component.new(:name => "Class[main]").pathbuilder.must == [""]
+ it "should produce the class name even for the class named main" do
+ component.new(:name => "Class[main]").pathbuilder.must == ["Main"]
end
it "should produce a resource reference if the component does not model a class" do
diff --git a/spec/unit/type/exec_spec.rb b/spec/unit/type/exec_spec.rb
index 85a5809dc..6d780b3ff 100755
--- a/spec/unit/type/exec_spec.rb
+++ b/spec/unit/type/exec_spec.rb
@@ -8,6 +8,8 @@ describe Puppet::Type.type(:exec) do
Puppet.features.stubs(:root?).returns(true)
output = rest.delete(:output) || ''
+
+ output = Puppet::Util::Execution::ProcessOutput.new(output, exitstatus)
tries = rest[:tries] || 1
args = {
@@ -21,14 +23,12 @@ describe Puppet::Type.type(:exec) do
exec = Puppet::Type.type(:exec).new(args)
status = stub "process", :exitstatus => exitstatus
- Puppet::Util::SUIDManager.expects(:run_and_capture).times(tries).
+ Puppet::Util::Execution.expects(:execute).times(tries).
with() { |*args|
args[0] == command &&
- args[1] == nil &&
- args[2] == nil &&
- args[3][:override_locale] == false &&
- args[3].has_key?(:custom_environment)
- } .returns([output, status])
+ args[1][:override_locale] == false &&
+ args[1].has_key?(:custom_environment)
+ }.returns(output)
return exec
end
@@ -61,7 +61,7 @@ describe Puppet::Type.type(:exec) do
end
describe "when execing" do
- it "should use the 'run_and_capture' method to exec" do
+ it "should use the 'execute' method to exec" do
exec_tester("true").refresh.should == :executed_command
end
@@ -753,4 +753,11 @@ describe Puppet::Type.type(:exec) do
type.new(:command => abs, :path => path).must be
end
end
+ describe "when providing a umask" do
+ it "should fail if an invalid umask is used" do
+ resource = Puppet::Type.type(:exec).new :command => @command
+ expect { resource[:umask] = '0028'}.to raise_error(Puppet::ResourceError, /umask specification is invalid/)
+ expect { resource[:umask] = '28' }.to raise_error(Puppet::ResourceError, /umask specification is invalid/)
+ end
+ end
end
diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb
index c1c97a452..5a73dceb1 100755
--- a/spec/unit/type/file/content_spec.rb
+++ b/spec/unit/type/file/content_spec.rb
@@ -84,6 +84,13 @@ describe content do
@content.should.must == string
end
+
+ it "should convert the value to ASCII-8BIT", :if => "".respond_to?(:encode) do
+ @content = content.new(:resource => @resource)
+ @content.should= "Let's make a \u{2603}"
+
+ @content.actual_content.should == "Let's make a \xE2\x98\x83".force_encoding(Encoding::ASCII_8BIT)
+ end
end
describe "when retrieving the current content" do
@@ -329,8 +336,10 @@ describe content do
end
it "should copy content from the source to the file" do
+ dest_file = Puppet::FileSystem::File.new(@filename)
@resource.write(@source)
- IO.binread(@filename).should == @source_content
+
+ dest_file.binread.should == @source_content
end
it "should return the checksum computed" do
@@ -358,8 +367,10 @@ describe content do
end
it "should write the contents to the file" do
+ dest_file = Puppet::FileSystem::File.new(@filename)
@resource.write(@source)
- IO.binread(@filename).should == @source_content
+
+ dest_file.binread.should == @source_content
end
it "should not write anything if source is not found" do
diff --git a/spec/unit/type/file/ctime_spec.rb b/spec/unit/type/file/ctime_spec.rb
index ba46da286..eea0f1f92 100755
--- a/spec/unit/type/file/ctime_spec.rb
+++ b/spec/unit/type/file/ctime_spec.rb
@@ -16,7 +16,7 @@ describe Puppet::Type.type(:file).attrclass(:ctime) do
@resource[:audit] = [:ctime]
# this .to_resource audit behavior is magical :-(
- @resource.to_resource[:ctime].should == File.stat(@filename).ctime
+ @resource.to_resource[:ctime].should == Puppet::FileSystem::File.new(@filename).stat.ctime
end
it "should return absent if auditing an absent file" do
diff --git a/spec/unit/type/file/mode_spec.rb b/spec/unit/type/file/mode_spec.rb
index dc43ae498..8663fe57d 100755
--- a/spec/unit/type/file/mode_spec.rb
+++ b/spec/unit/type/file/mode_spec.rb
@@ -18,6 +18,10 @@ describe Puppet::Type.type(:file).attrclass(:mode) do
expect { mode.value = '0755' }.not_to raise_error
end
+ it "should accept valid symbolic strings" do
+ expect { mode.value = 'g+w,u-x' }.not_to raise_error
+ end
+
it "should not accept strings other than octal numbers" do
expect do
mode.value = 'readable please!'
@@ -35,6 +39,10 @@ describe Puppet::Type.type(:file).attrclass(:mode) do
mode.munge('0644').should == '644'
end
+ it "should accept symbolic strings as arguments and return them intact" do
+ mode.munge('u=rw,go=r').should == 'u=rw,go=r'
+ end
+
it "should accept integers are arguments" do
mode.munge(0644).should == '644'
end
@@ -72,11 +80,34 @@ describe Puppet::Type.type(:file).attrclass(:mode) do
mode.must_not be_insync('755')
end
- it "should return true if the file is a link and we are managing links", :unless => Puppet.features.microsoft_windows? do
- File.symlink('anything', path)
+ it "should return true if the file is a link and we are managing links", :if => Puppet.features.manages_symlinks? do
+ Puppet::FileSystem::File.new('anything').symlink(path)
mode.must be_insync('644')
end
+
+ describe "with a symbolic mode" do
+ let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'u+w,g-w' }
+ let(:mode_sym) { resource_sym.property(:mode) }
+
+ it "should return true if the mode matches, regardless of other bits" do
+ FileUtils.touch(path)
+
+ mode_sym.must be_insync('644')
+ end
+
+ it "should return false if the mode requires 0's where there are 1's" do
+ FileUtils.touch(path)
+
+ mode_sym.must_not be_insync('624')
+ end
+
+ it "should return false if the mode requires 1's where there are 0's" do
+ FileUtils.touch(path)
+
+ mode_sym.must_not be_insync('044')
+ end
+ end
end
describe "#retrieve" do
@@ -145,4 +176,19 @@ describe Puppet::Type.type(:file).attrclass(:mode) do
end
end
end
+
+ describe "#sync with a symbolic mode" do
+ let(:resource_sym) { Puppet::Type.type(:file).new :path => path, :mode => 'u+w,g-w' }
+ let(:mode_sym) { resource_sym.property(:mode) }
+
+ before { FileUtils.touch(path) }
+
+ it "changes only the requested bits" do
+ # lower nibble must be set to 4 for the sake of passing on Windows
+ FileUtils.chmod 0464, path
+ mode_sym.sync
+ file = Puppet::FileSystem::File.new(path)
+ (file.stat.mode & 0777).to_s(8).should == "644"
+ end
+ end
end
diff --git a/spec/unit/type/file/mtime_spec.rb b/spec/unit/type/file/mtime_spec.rb
index 93abcb2fe..a20bdf196 100755
--- a/spec/unit/type/file/mtime_spec.rb
+++ b/spec/unit/type/file/mtime_spec.rb
@@ -16,7 +16,7 @@ describe Puppet::Type.type(:file).attrclass(:mtime) do
@resource[:audit] = [:mtime]
# this .to_resource audit behavior is magical :-(
- @resource.to_resource[:mtime].should == File.stat(@filename).mtime
+ @resource.to_resource[:mtime].should == Puppet::FileSystem::File.new(@filename).stat.mtime
end
it "should return absent if auditing an absent file" do
diff --git a/spec/unit/type/file/source_spec.rb b/spec/unit/type/file/source_spec.rb
index 36cad8849..e645842c0 100755
--- a/spec/unit/type/file/source_spec.rb
+++ b/spec/unit/type/file/source_spec.rb
@@ -154,11 +154,10 @@ describe Puppet::Type.type(:file).attrclass(:source) do
describe "when copying the source values" do
before do
-
@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 => @foobar
+ @metadata = stub 'metadata', :owner => 100, :group => 200, :mode => "173", :checksum => "{md5}asdfasdf", :ftype => "file", :source => @foobar
@source.stubs(:metadata).returns @metadata
Puppet.features.stubs(:root?).returns true
@@ -202,7 +201,7 @@ describe Puppet::Type.type(:file).attrclass(:source) do
@resource[:content].must == @metadata.checksum
end
- it "should not copy the metadata's owner to the resource if it is already set" do
+ it "should not copy the metadata's owner, group, checksum and mode to the resource if they are already set" do
@resource[:owner] = 1
@resource[:group] = 2
@resource[:mode] = 3
@@ -217,26 +216,153 @@ describe Puppet::Type.type(:file).attrclass(:source) do
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
+ before do
+ Puppet.features.stubs(:root?).returns false
+ end
+ it "should not try to set the owner" do
@source.copy_source_values
@resource[:owner].should be_nil
end
+
+ it "should not try to set the group" do
+ @source.copy_source_values
+ @resource[:group].should be_nil
+ end
+ end
+
+ context "when source_permissions is `use_when_creating`" do
+ before :each do
+ @resource[:source_permissions] = "use_when_creating"
+ Puppet.features.expects(:root?).returns true
+ @source.stubs(:local?).returns(false)
+ end
+
+ context "when managing a new file" do
+ it "should copy owner and group from local sources" do
+ @source.stubs(:local?).returns true
+
+ @source.copy_source_values
+
+ @resource[:owner].must == 100
+ @resource[:group].must == 200
+ @resource[:mode].must == "173"
+ end
+
+ it "copies the remote owner" do
+ @source.copy_source_values
+
+ @resource[:owner].must == 100
+ end
+
+ it "copies the remote group" do
+ @source.copy_source_values
+
+ @resource[:group].must == 200
+ end
+
+ it "copies the remote mode" do
+ @source.copy_source_values
+
+ @resource[:mode].must == "173"
+ end
+ end
+
+ context "when managing an existing file" do
+ before :each do
+ Puppet::FileSystem::File.stubs(:exist?).with(@resource[:path]).returns(true)
+ end
+
+ it "should not copy owner, group or mode from local sources" do
+ @source.stubs(:local?).returns true
+
+ @source.copy_source_values
+
+ @resource[:owner].must be_nil
+ @resource[:group].must be_nil
+ @resource[:mode].must be_nil
+ end
+
+ it "preserves the local owner" do
+ @source.copy_source_values
+
+ @resource[:owner].must be_nil
+ end
+
+ it "preserves the local group" do
+ @source.copy_source_values
+
+ @resource[:group].must be_nil
+ end
+
+ it "preserves the local mode" do
+ @source.copy_source_values
+
+ @resource[:mode].must be_nil
+ end
+ end
+ end
+
+ context "when source_permissions is `ignore`" do
+ before :each do
+ @resource[:source_permissions] = "ignore"
+ @source.stubs(:local?).returns(false)
+ Puppet.features.expects(:root?).returns true
+ end
+
+ it "should not copy owner, group or mode from local sources" do
+ @source.stubs(:local?).returns true
+
+ @source.copy_source_values
+
+ @resource[:owner].must be_nil
+ @resource[:group].must be_nil
+ @resource[:mode].must be_nil
+ end
+
+ it "preserves the local owner" do
+ @source.copy_source_values
+
+ @resource[:owner].must be_nil
+ end
+
+ it "preserves the local group" do
+ @source.copy_source_values
+
+ @resource[:group].must be_nil
+ end
+
+ it "preserves the local mode" do
+ @source.copy_source_values
+
+ @resource[:mode].must be_nil
+ end
end
describe "on Windows" do
before :each do
Puppet.features.stubs(:microsoft_windows?).returns true
end
+ let(:deprecation_message) { "Copying owner/mode/group from the" <<
+ " source file on Windows is deprecated;" <<
+ " use source_permissions => ignore." }
- it "should not copy owner and group from remote sources" do
+ it "should copy only mode from remote sources" do
@source.stubs(:local?).returns false
@source.copy_source_values
@resource[:owner].must be_nil
@resource[:group].must be_nil
+ @resource[:mode].must == "173"
+ end
+
+ it "should copy mode from remote sources" do
+ @source.stubs(:local?).returns false
+
+ @source.copy_source_values
+
+ @resource[:mode].must == "173"
end
it "should copy owner and group from local sources" do
@@ -246,6 +372,51 @@ describe Puppet::Type.type(:file).attrclass(:source) do
@resource[:owner].must == 100
@resource[:group].must == 200
+ @resource[:mode].must == "173"
+ end
+
+ it "should issue deprecation warning when copying metadata from remote sources when group, owner, and mode are unspecified" do
+ @source.stubs(:local?).returns false
+ Puppet.expects(:deprecation_warning).with(deprecation_message).at_least_once
+
+ @source.copy_source_values
+ end
+
+ it "should issue deprecation warning when copying metadata from remote sources if only user is unspecified" do
+ @source.stubs(:local?).returns false
+ Puppet.expects(:deprecation_warning).with(deprecation_message).at_least_once
+ @resource[:group] = 2
+ @resource[:mode] = 3
+
+ @source.copy_source_values
+ end
+
+ it "should issue deprecation warning when copying metadata from remote sources if only group is unspecified" do
+ @source.stubs(:local?).returns false
+ Puppet.expects(:deprecation_warning).with(deprecation_message).at_least_once
+ @resource[:owner] = 1
+ @resource[:mode] = 3
+
+ @source.copy_source_values
+ end
+
+ it "should issue deprecation warning when copying metadata from remote sources if only mode is unspecified" do
+ @source.stubs(:local?).returns false
+ Puppet.expects(:deprecation_warning).with(deprecation_message).at_least_once
+ @resource[:owner] = 1
+ @resource[:group] = 2
+
+ @source.copy_source_values
+ end
+
+ it "should not issue deprecation warning when copying metadata from remote sources if group, owner, and mode are all specified" do
+ @source.stubs(:local?).returns false
+ Puppet.expects(:deprecation_warning).with(deprecation_message).never
+ @resource[:owner] = 1
+ @resource[:group] = 2
+ @resource[:mode] = 3
+
+ @source.copy_source_values
end
end
end
@@ -358,5 +529,4 @@ describe Puppet::Type.type(:file).attrclass(:source) do
end
end
end
-
end
diff --git a/spec/unit/type/file_spec.rb b/spec/unit/type/file_spec.rb
index dd695eeda..75b58926a 100755
--- a/spec/unit/type/file_spec.rb
+++ b/spec/unit/type/file_spec.rb
@@ -10,7 +10,6 @@ describe Puppet::Type.type(:file) do
let(:catalog) { Puppet::Resource::Catalog.new }
before do
- @real_posix = Puppet.features.posix?
Puppet.features.stubs("posix?").returns(true)
end
@@ -78,33 +77,29 @@ describe Puppet::Type.type(:file) do
end
describe "when using UNC filenames", :if => Puppet.features.microsoft_windows? do
- before :each do
- pending("UNC file paths not yet supported")
- end
-
it "should remove trailing slashes" do
- file[:path] = "//server/foo/bar/baz/"
- file[:path].should == "//server/foo/bar/baz"
+ file[:path] = "//localhost/foo/bar/baz/"
+ file[:path].should == "//localhost/foo/bar/baz"
end
it "should remove double slashes" do
- file[:path] = "//server/foo/bar//baz"
- file[:path].should == "//server/foo/bar/baz"
+ file[:path] = "//localhost/foo/bar//baz"
+ file[:path].should == "//localhost/foo/bar/baz"
end
it "should remove trailing double slashes" do
- file[:path] = "//server/foo/bar/baz//"
- file[:path].should == "//server/foo/bar/baz"
+ file[:path] = "//localhost/foo/bar/baz//"
+ file[:path].should == "//localhost/foo/bar/baz"
end
it "should remove a trailing slash from a sharename" do
- file[:path] = "//server/foo/"
- file[:path].should == "//server/foo"
+ file[:path] = "//localhost/foo/"
+ file[:path].should == "//localhost/foo"
end
it "should not modify a sharename" do
- file[:path] = "//server/foo"
- file[:path].should == "//server/foo"
+ file[:path] = "//localhost/foo"
+ file[:path].should == "//localhost/foo"
end
end
end
@@ -352,7 +347,7 @@ describe Puppet::Type.type(:file) do
file[:ensure].should == :file
end
- it "should set a desired 'ensure' value if none is set and 'target' is set" do
+ it "should set a desired 'ensure' value if none is set and 'target' is set", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
file = described_class.new(:path => path, :target => File.expand_path(__FILE__))
file[:ensure].should == :link
end
@@ -397,7 +392,7 @@ describe Puppet::Type.type(:file) do
:target => "some_target",
:source => File.expand_path("some_source"),
}.each do |param, value|
- it "should omit the #{param} parameter" do
+ it "should omit the #{param} parameter", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
# Make a new file, because we have to set the param at initialization
# or it wouldn't be copied regardless.
file = described_class.new(:path => path, param => value)
@@ -603,7 +598,7 @@ describe Puppet::Type.type(:file) do
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
+ it "should set the target to the full path of discovered file and set :ensure to :link if the file is not a directory", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
file.stubs(:perform_recursion).returns [@first, @second]
file.recurse_link("first" => @resource, "second" => file)
@@ -925,20 +920,20 @@ describe Puppet::Type.type(:file) do
file.remove_existing(:directory).should == true
- File.exists?(file[:path]).should == false
+ Puppet::FileSystem::File.exist?(file[:path]).should == false
end
- it "should remove an existing link", :unless => Puppet.features.microsoft_windows? do
+ it "should remove an existing link", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
file.stubs(:perform_backup).returns true
target = tmpfile('link_target')
FileUtils.touch(target)
- FileUtils.symlink(target, path)
+ Puppet::FileSystem::File.new(target).symlink(path)
file[:target] = target
file.remove_existing(:directory).should == true
- File.exists?(file[:path]).should == false
+ Puppet::FileSystem::File.exist?(file[:path]).should == false
end
it "should fail if the file is not a file, link, or directory" do
@@ -952,7 +947,7 @@ describe Puppet::Type.type(:file) do
file.stat
file.stubs(:stat).returns stub('stat', :ftype => 'file')
- File.stubs(:unlink)
+ Puppet::FileSystem::File.stubs(:unlink)
file.remove_existing(:directory).should == true
file.instance_variable_get(:@stat).should == :needs_stat
@@ -1010,11 +1005,11 @@ describe Puppet::Type.type(:file) do
end
end
- describe "#stat", :unless => Puppet.features.microsoft_windows? do
+ describe "#stat", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
before do
target = tmpfile('link_target')
FileUtils.touch(target)
- FileUtils.symlink(target, path)
+ Puppet::FileSystem::File.new(target).symlink(path)
file[:target] = target
file[:links] = :manage # so we always use :lstat
@@ -1033,7 +1028,7 @@ describe Puppet::Type.type(:file) do
end
it "should return nil if the file does not exist" do
- file[:path] = '/foo/bar/baz/non-existent'
+ file[:path] = make_absolute('/foo/bar/baz/non-existent')
file.stat.should be_nil
end
@@ -1207,7 +1202,7 @@ describe Puppet::Type.type(:file) do
describe "when autorequiring" do
describe "target" do
- it "should require file resource when specified with the target property" do
+ it "should require file resource when specified with the target property", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
file = described_class.new(:path => File.expand_path("/foo"), :ensure => :directory)
link = described_class.new(:path => File.expand_path("/bar"), :ensure => :link, :target => File.expand_path("/foo"))
catalog.add_resource file
@@ -1229,7 +1224,7 @@ describe Puppet::Type.type(:file) do
reqs[0].target.must == link
end
- it "should not require target if target is not managed" do
+ it "should not require target if target is not managed", :if => described_class.defaultprovider.feature?(:manages_symlinks) do
link = described_class.new(:path => File.expand_path('/foo'), :ensure => :link, :target => '/bar')
catalog.add_resource link
link.autorequire.size.should == 0
@@ -1272,8 +1267,8 @@ describe Puppet::Type.type(:file) do
describe "on Windows systems", :if => Puppet.features.microsoft_windows? do
describe "when using UNC filenames" do
it "should autorequire its parent directory" do
- file[:path] = '//server/foo/bar/baz'
- dir = described_class.new(:path => "//server/foo/bar")
+ file[:path] = '//localhost/foo/bar/baz'
+ dir = described_class.new(:path => "//localhost/foo/bar")
catalog.add_resource file
catalog.add_resource dir
reqs = file.autorequire
@@ -1282,9 +1277,9 @@ describe Puppet::Type.type(:file) do
end
it "should autorequire its nearest ancestor directory" do
- file = described_class.new(:path => "//server/foo/bar/baz/qux")
- dir = described_class.new(:path => "//server/foo/bar/baz")
- grandparent = described_class.new(:path => "//server/foo/bar")
+ file = described_class.new(:path => "//localhost/foo/bar/baz/qux")
+ dir = described_class.new(:path => "//localhost/foo/bar/baz")
+ grandparent = described_class.new(:path => "//localhost/foo/bar")
catalog.add_resource file
catalog.add_resource dir
catalog.add_resource grandparent
@@ -1295,13 +1290,13 @@ describe Puppet::Type.type(:file) do
end
it "should not autorequire anything when there is no nearest ancestor directory" do
- file = described_class.new(:path => "//server/foo/bar/baz/qux")
+ file = described_class.new(:path => "//localhost/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 = described_class.new(:path => "//server/foo")
+ file = described_class.new(:path => "//localhost/foo")
catalog.add_resource file
puts file.autorequire
file.autorequire.should be_empty
@@ -1311,49 +1306,46 @@ describe Puppet::Type.type(:file) do
end
end
- describe "when managing links" do
+ describe "when managing links", :if => Puppet.features.manages_symlinks? do
require 'tempfile'
- if @real_posix
- describe "on POSIX systems" do
- before do
- Dir.mkdir(path)
- @target = File.join(path, "target")
- @link = File.join(path, "link")
-
- File.open(@target, "w", 0644) { |f| f.puts "yayness" }
- File.symlink(@target, @link)
-
- file[:path] = @link
- file[:mode] = 0755
-
- catalog.add_resource file
- end
+ before :each do
+ Dir.mkdir(path)
+ @target = File.join(path, "target")
+ @link = File.join(path, "link")
+
+ target = described_class.new(
+ :ensure => :file, :path => @target,
+ :catalog => catalog, :content => 'yayness',
+ :mode => 0644)
+ catalog.add_resource target
+
+ @link_resource = described_class.new(
+ :ensure => :link, :path => @link,
+ :target => @target, :catalog => catalog,
+ :mode => 0755)
+ catalog.add_resource @link_resource
- it "should default to managing the link" do
- catalog.apply
- # I convert them to strings so they display correctly if there's an error.
- (File.stat(@target).mode & 007777).to_s(8).should == '644'
- end
+ # to prevent the catalog from trying to write state.yaml
+ Puppet::Util::Storage.stubs(:store)
+ end
- it "should be able to follow links" do
- file[:links] = :follow
- catalog.apply
+ it "should preserve the original file mode and ignore the one set by the link" do
+ @link_resource[:links] = :manage # default
+ catalog.apply
- (File.stat(@target).mode & 007777).to_s(8).should == '755'
- end
- end
- else # @real_posix
- # should recode tests using expectations instead of using the filesystem
+ # I convert them to strings so they display correctly if there's an error.
+ (Puppet::FileSystem::File.new(@target).stat.mode & 007777).to_s(8).should == '644'
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 manage the mode of the followed link" do
+ pending("Windows cannot presently manage the mode when following symlinks",
+ :if => Puppet.features.microsoft_windows?) do
+ @link_resource[:links] = :follow
+ catalog.apply
- it "should refuse to work with links"
+ (Puppet::FileSystem::File.new(@target).stat.mode & 007777).to_s(8).should == '755'
+ end
end
end
@@ -1436,7 +1428,7 @@ describe Puppet::Type.type(:file) do
catalog.apply
- File.should be_exist(path)
+ Puppet::FileSystem::File.exist?(path).should be_true
@logs.should_not be_any {|l| l.level != :notice }
end
end
diff --git a/spec/unit/type/group_spec.rb b/spec/unit/type/group_spec.rb
index f8d24f0df..55f6e548b 100755
--- a/spec/unit/type/group_spec.rb
+++ b/spec/unit/type/group_spec.rb
@@ -61,4 +61,24 @@ describe Puppet::Type.type(:group) do
type.exists?.should == true
end
+
+ describe "should delegate :members implementation to the provider:" do
+
+ let (:provider) { @class.provide(:testing) { has_features :manages_members } }
+ let (:provider_instance) { provider.new }
+ let (:type) { @class.new(:name => "group", :provider => provider_instance, :members => ['user1']) }
+
+ it "insync? calls members_insync?" do
+ provider_instance.expects(:members_insync?).with(['user1'], ['user1']).returns true
+ type.property(:members).insync?(['user1']).should be_true
+ end
+
+ it "is_to_s and should_to_s call members_to_s" do
+ provider_instance.expects(:members_to_s).with(['user2', 'user1']).returns "user2 (), user1 ()"
+ provider_instance.expects(:members_to_s).with(['user1']).returns "user1 ()"
+
+ type.property(:members).is_to_s('user1').should == 'user1 ()'
+ type.property(:members).should_to_s('user2,user1').should == 'user2 (), user1 ()'
+ end
+ end
end
diff --git a/spec/unit/type/k5login_spec.rb b/spec/unit/type/k5login_spec.rb
index 2524d98c5..484ddf8e7 100755
--- a/spec/unit/type/k5login_spec.rb
+++ b/spec/unit/type/k5login_spec.rb
@@ -46,7 +46,7 @@ describe Puppet::Type.type(:k5login), :unless => Puppet.features.microsoft_windo
it "should create the file when synced" do
resource(:ensure => 'present').parameter(:ensure).sync
- File.should be_exist path
+ Puppet::FileSystem::File.exist?(path).should be_true
end
end
@@ -83,7 +83,7 @@ describe Puppet::Type.type(:k5login), :unless => Puppet.features.microsoft_windo
it "should remove the file ensure is absent" do
resource(:ensure => 'absent').property(:ensure).sync
- File.should_not be_exist path
+ Puppet::FileSystem::File.exist?(path).should be_false
end
it "should write one principal to the file" do
@@ -106,7 +106,7 @@ describe Puppet::Type.type(:k5login), :unless => Puppet.features.microsoft_windo
it "should update the mode to #{mode}" do
resource(:mode => mode).property(:mode).sync
- (File.stat(path).mode & 07777).to_s(8).should == mode
+ (Puppet::FileSystem::File.new(path).stat.mode & 07777).to_s(8).should == mode
end
end
end
diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb
index 726577891..75b2f505d 100755
--- a/spec/unit/type/mount_spec.rb
+++ b/spec/unit/type/mount_spec.rb
@@ -536,4 +536,57 @@ describe Puppet::Type.type(:mount), :unless => Puppet.features.microsoft_windows
run_in_catalog(resource)
end
end
+
+ describe "establishing autorequires" do
+
+ def create_resource(path)
+ described_class.new(
+ :name => path,
+ :provider => providerclass.new(path)
+ )
+ end
+
+ def create_catalog(*resources)
+ catalog = Puppet::Resource::Catalog.new
+ resources.each do |resource|
+ catalog.add_resource resource
+ end
+
+ catalog
+ end
+
+ let(:root_mount) { create_resource("/") }
+ let(:var_mount) { create_resource("/var") }
+ let(:log_mount) { create_resource("/var/log") }
+
+ before do
+ create_catalog(root_mount, var_mount, log_mount)
+ end
+
+ it "adds no autorequires for the root mount" do
+ expect(root_mount.autorequire).to be_empty
+ end
+
+ it "adds the parent autorequire for a mount with one parent" do
+ parent_relationship = var_mount.autorequire[0]
+
+ expect(var_mount.autorequire).to have_exactly(1).item
+
+ expect(parent_relationship.source).to eq root_mount
+ expect(parent_relationship.target).to eq var_mount
+ end
+
+ it "adds both parent autorequires for a mount with two parents" do
+ grandparent_relationship = log_mount.autorequire[0]
+ parent_relationship = log_mount.autorequire[1]
+
+ expect(log_mount.autorequire).to have_exactly(2).items
+
+ expect(grandparent_relationship.source).to eq root_mount
+ expect(grandparent_relationship.target).to eq log_mount
+
+ expect(parent_relationship.source).to eq var_mount
+ expect(parent_relationship.target).to eq log_mount
+ end
+ end
end
diff --git a/spec/unit/type/nagios_spec.rb b/spec/unit/type/nagios_spec.rb
index 8c86aae7f..84d4338de 100755
--- a/spec/unit/type/nagios_spec.rb
+++ b/spec/unit/type/nagios_spec.rb
@@ -3,6 +3,222 @@ require 'spec_helper'
require 'puppet/external/nagios'
+describe "Nagios parser" do
+
+ NONESCAPED_SEMICOLON_COMMENT = <<-'EOL'
+define host{
+ use linux-server ; Name of host template to use
+ host_name localhost
+ alias localhost
+ address 127.0.0.1
+ }
+
+define command{
+ command_name notify-host-by-email
+ command_line /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" $CONTACTEMAIL$
+ }
+EOL
+
+ LINE_COMMENT_SNIPPET = <<-'EOL'
+
+# This is a comment starting at the beginning of a line
+
+define command{
+
+# This is a comment starting at the beginning of a line
+
+ command_name command_name
+
+# This is a comment starting at the beginning of a line
+ ## --PUPPET_NAME-- (called '_naginator_name' in the manifest) command_name
+
+ command_line command_line
+
+# This is a comment starting at the beginning of a line
+
+ }
+
+# This is a comment starting at the beginning of a line
+
+EOL
+
+ LINE_COMMENT_SNIPPET2 = <<-'EOL'
+ define host{
+ use linux-server ; Name of host template to use
+ host_name localhost
+ alias localhost
+ address 127.0.0.1
+ }
+define command{
+ command_name command_name2
+ command_line command_line2
+ }
+EOL
+
+ UNKNOWN_NAGIOS_OBJECT_DEFINITION = <<-'EOL'
+ define command2{
+ command_name notify-host-by-email
+ command_line /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" $CONTACTEMAIL$
+ }
+ EOL
+
+ MISSING_CLOSING_CURLY_BRACKET = <<-'EOL'
+ define command{
+ command_name notify-host-by-email
+ command_line /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" $CONTACTEMAIL$
+ EOL
+
+ ESCAPED_SEMICOLON = <<-'EOL'
+ define command {
+ command_name nagios_table_size
+ command_line $USER3$/check_mysql_health --hostname localhost --username nagioschecks --password nagiosCheckPWD --mode sql --name "SELECT ROUND(Data_length/1024) as Data_kBytes from INFORMATION_SCHEMA.TABLES where TABLE_NAME=\"$ARG1$\"\;" --name2 "table size" --units kBytes -w $ARG2$ -c $ARG3$
+ }
+ EOL
+
+ POUND_SIGN_HASH_SYMBOL_NOT_IN_FIRST_COLUMN = <<-'EOL'
+ define command {
+ command_name notify-by-irc
+ command_line /usr/local/bin/riseup-nagios-client.pl "$HOSTNAME$ ($SERVICEDESC$) $NOTIFICATIONTYPE$ #$SERVICEATTEMPT$ $SERVICESTATETYPE$ $SERVICEEXECUTIONTIME$s $SERVICELATENCY$s $SERVICEOUTPUT$ $SERVICEPERFDATA$"
+ }
+ EOL
+
+ ANOTHER_ESCAPED_SEMICOLON = <<-EOL
+define command {
+\tcommand_line LC_ALL=en_US.UTF-8 /usr/lib/nagios/plugins/check_haproxy -u 'http://blah:blah@$HOSTADDRESS$:8080/haproxy?stats\\;csv'
+\tcommand_name check_haproxy
+}
+EOL
+
+ it "should parse without error" do
+ parser = Nagios::Parser.new
+ expect {
+ results = parser.parse(NONESCAPED_SEMICOLON_COMMENT)
+ }.to_not raise_error
+ end
+
+ describe "when parsing a statement" do
+ parser = Nagios::Parser.new
+ results = parser.parse(NONESCAPED_SEMICOLON_COMMENT)
+ results.each do |obj|
+ it "should have the proper base type" do
+ obj.should be_a_kind_of(Nagios::Base)
+ end
+ end
+ end
+
+ it "should raise an error when an incorrect object definition is present" do
+ parser = Nagios::Parser.new
+ expect {
+ results = parser.parse(UNKNOWN_NAGIOS_OBJECT_DEFINITION)
+ }.to raise_error Nagios::Base::UnknownNagiosType
+ end
+
+ it "should raise an error when syntax is not correct" do
+ parser = Nagios::Parser.new
+ expect {
+ results = parser.parse(MISSING_CLOSING_CURLY_BRACKET)
+ }.to raise_error Nagios::Parser::SyntaxError
+ end
+
+ describe "when encoutering ';'" do
+ it "should not throw an exception" do
+ parser = Nagios::Parser.new
+ expect {
+ results = parser.parse(ESCAPED_SEMICOLON)
+ }.to_not raise_error Nagios::Parser::SyntaxError
+ end
+
+ it "should ignore it if it is a comment" do
+ parser = Nagios::Parser.new
+ results = parser.parse(NONESCAPED_SEMICOLON_COMMENT)
+ results[0].use.should eql("linux-server")
+ end
+
+ it "should parse correctly if it is escaped" do
+ parser = Nagios::Parser.new
+ results = parser.parse(ESCAPED_SEMICOLON)
+ results[0].command_line.should eql("$USER3$/check_mysql_health --hostname localhost --username nagioschecks --password nagiosCheckPWD --mode sql --name \"SELECT ROUND(Data_length/1024) as Data_kBytes from INFORMATION_SCHEMA.TABLES where TABLE_NAME=\\\"$ARG1$\\\";\" --name2 \"table size\" --units kBytes -w $ARG2$ -c $ARG3$")
+ end
+ end
+
+ describe "when encountering '#'" do
+
+ it "should not throw an exception" do
+ parser = Nagios::Parser.new
+ expect {
+ results = parser.parse(POUND_SIGN_HASH_SYMBOL_NOT_IN_FIRST_COLUMN)
+ }.to_not raise_error Nagios::Parser::SyntaxError
+ end
+
+
+ it "should ignore it at the beginning of a line" do
+ parser = Nagios::Parser.new
+ results = parser.parse(LINE_COMMENT_SNIPPET)
+ results[0].command_line.should eql("command_line")
+ end
+
+ it "should let it go anywhere else" do
+ parser = Nagios::Parser.new
+ results = parser.parse(POUND_SIGN_HASH_SYMBOL_NOT_IN_FIRST_COLUMN)
+ results[0].command_line.should eql("/usr/local/bin/riseup-nagios-client.pl \"$HOSTNAME$ ($SERVICEDESC$) $NOTIFICATIONTYPE$ \#$SERVICEATTEMPT$ $SERVICESTATETYPE$ $SERVICEEXECUTIONTIME$s $SERVICELATENCY$s $SERVICEOUTPUT$ $SERVICEPERFDATA$\"")
+ end
+
+ end
+
+ describe "when encountering ';' again" do
+ it "should not throw an exception" do
+ parser = Nagios::Parser.new
+ expect {
+ results = parser.parse(ANOTHER_ESCAPED_SEMICOLON)
+ }.to_not raise_error Nagios::Parser::SyntaxError
+ end
+
+ it "should parse correctly" do
+ parser = Nagios::Parser.new
+ results = parser.parse(ANOTHER_ESCAPED_SEMICOLON)
+ results[0].command_line.should eql("LC_ALL=en_US.UTF-8 /usr/lib/nagios/plugins/check_haproxy -u 'http://blah:blah@$HOSTADDRESS$:8080/haproxy?stats;csv'")
+ end
+ end
+
+
+ it "should be idempotent" do
+ parser = Nagios::Parser.new
+ src = ANOTHER_ESCAPED_SEMICOLON.dup
+ results = parser.parse(src)
+ nagios_type = Nagios::Base.create(:command)
+ nagios_type.command_name = results[0].command_name
+ nagios_type.command_line = results[0].command_line
+ nagios_type.to_s.should eql(ANOTHER_ESCAPED_SEMICOLON)
+ end
+
+end
+
+describe "Nagios generator" do
+
+ it "should escape ';'" do
+ param = '$USER3$/check_mysql_health --hostname localhost --username nagioschecks --password nagiosCheckPWD --mode sql --name "SELECT ROUND(Data_length/1024) as Data_kBytes from INFORMATION_SCHEMA.TABLES where TABLE_NAME=\"$ARG1$\";" --name2 "table size" --units kBytes -w $ARG2$ -c $ARG3$'
+ nagios_type = Nagios::Base.create(:command)
+ nagios_type.command_line = param
+ nagios_type.to_s.should eql("define command {\n\tcommand_line $USER3$/check_mysql_health --hostname localhost --username nagioschecks --password nagiosCheckPWD --mode sql --name \"SELECT ROUND(Data_length/1024) as Data_kBytes from INFORMATION_SCHEMA.TABLES where TABLE_NAME=\\\"$ARG1$\\\"\\;\" --name2 \"table size\" --units kBytes -w $ARG2$ -c $ARG3$\n}\n")
+ end
+
+ it "should escape ';' if it is not already the case" do
+ param = "LC_ALL=en_US.UTF-8 /usr/lib/nagios/plugins/check_haproxy -u 'http://blah:blah@$HOSTADDRESS$:8080/haproxy?stats;csv'"
+ nagios_type = Nagios::Base.create(:command)
+ nagios_type.command_line = param
+ nagios_type.to_s.should eql("define command {\n\tcommand_line LC_ALL=en_US.UTF-8 /usr/lib/nagios/plugins/check_haproxy -u 'http://blah:blah@$HOSTADDRESS$:8080/haproxy?stats\\;csv'\n}\n")
+ end
+
+ it "should be idempotent" do
+ param = '$USER3$/check_mysql_health --hostname localhost --username nagioschecks --password nagiosCheckPWD --mode sql --name "SELECT ROUND(Data_length/1024) as Data_kBytes from INFORMATION_SCHEMA.TABLES where TABLE_NAME=\"$ARG1$\";" --name2 "table size" --units kBytes -w $ARG2$ -c $ARG3$'
+ nagios_type = Nagios::Base.create(:command)
+ nagios_type.command_line = param
+ parser = Nagios::Parser.new
+ results = parser.parse(nagios_type.to_s)
+ results[0].command_line.should eql(param)
+ end
+end
+
describe "Nagios resource types" do
Nagios::Base.eachtype do |name, nagios_type|
puppet_type = Puppet::Type.type("nagios_#{name}")
diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb
index 5312b3f38..fefd15805 100755
--- a/spec/unit/type/package_spec.rb
+++ b/spec/unit/type/package_spec.rb
@@ -51,7 +51,7 @@ describe Puppet::Type.type(:package) do
:clear => nil,
:validate_source => nil
)
- Puppet::Type.type(:package).defaultprovider.expects(:new).returns(@provider)
+ Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider)
end
it "should support :present as a value to :ensure" do
@@ -100,6 +100,12 @@ describe Puppet::Type.type(:package) do
it "should accept any string as an argument to :source" do
expect { Puppet::Type.type(:package).new(:name => "yay", :source => "stuff") }.to_not raise_error
end
+
+ it "should not accept a non-string name" do
+ expect do
+ Puppet::Type.type(:package).new(:name => ["error"])
+ end.to raise_error(Puppet::ResourceError, /Name must be a String/)
+ end
end
module PackageEvaluationTesting
diff --git a/spec/unit/type/schedule_spec.rb b/spec/unit/type/schedule_spec.rb
index 020f7d841..0610bd175 100755
--- a/spec/unit/type/schedule_spec.rb
+++ b/spec/unit/type/schedule_spec.rb
@@ -62,6 +62,12 @@ describe Puppet::Type.type(:schedule) do
end
end
+ it "should not produce default schedules when default_schedules is false" do
+ Puppet[:default_schedules] = false
+ schedules = Puppet::Type.type(:schedule).mkdefaultschedules
+ schedules.must have_exactly(0).items
+ end
+
it "should produce a schedule named puppet with a period of hourly and a repeat of 2" do
schedules = Puppet::Type.type(:schedule).mkdefaultschedules
schedules.find { |s|
diff --git a/spec/unit/type/service_spec.rb b/spec/unit/type/service_spec.rb
index 630b44b75..e36d11a56 100755
--- a/spec/unit/type/service_spec.rb
+++ b/spec/unit/type/service_spec.rb
@@ -120,14 +120,14 @@ describe Puppet::Type.type(:service), "when validating attribute values" do
end
it "should split paths on '#{File::PATH_SEPARATOR}'" do
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
FileTest.stubs(:directory?).returns(true)
svc = Puppet::Type.type(:service).new(:name => "yay", :path => "/one/two#{File::PATH_SEPARATOR}/three/four")
svc[:path].should == %w{/one/two /three/four}
end
it "should accept arrays of paths joined by '#{File::PATH_SEPARATOR}'" do
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
FileTest.stubs(:directory?).returns(true)
svc = Puppet::Type.type(:service).new(:name => "yay", :path => ["/one#{File::PATH_SEPARATOR}/two", "/three#{File::PATH_SEPARATOR}/four"])
svc[:path].should == %w{/one /two /three /four}
@@ -137,7 +137,7 @@ end
describe Puppet::Type.type(:service), "when setting default attribute values" do
it "should default to the provider's default path if one is available" do
FileTest.stubs(:directory?).returns(true)
- FileTest.stubs(:exist?).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).returns(true)
Puppet::Type.type(:service).defaultprovider.stubs(:respond_to?).returns(true)
Puppet::Type.type(:service).defaultprovider.stubs(:defpath).returns("testing")
diff --git a/spec/unit/type/tidy_spec.rb b/spec/unit/type/tidy_spec.rb
index cf3fc828b..abe5d26ff 100755
--- a/spec/unit/type/tidy_spec.rb
+++ b/spec/unit/type/tidy_spec.rb
@@ -10,17 +10,15 @@ describe tidy do
before do
@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"
+ path = '/foo/bar'
+ resource = tidy.new :path => path, :age => "1d"
stat = mock 'stat'
- File.expects(:lstat).with("/foo/bar").returns stat
- resource.stat("/foo/bar").should == stat
+ stub_file = stub(path, :lstat => stat)
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
+ resource.stat(path).should == stat
end
[:age, :size, :path, :matches, :type, :recurse, :rmdirs].each do |param|
@@ -130,7 +128,8 @@ describe tidy do
before do
@tidy = Puppet::Type.type(:tidy).new :path => @basepath
@stat = stub 'stat', :ftype => "directory"
- File.stubs(:lstat).with(@basepath).returns @stat
+ @stub_file = stub(@basepath, :lstat => @stat)
+ Puppet::FileSystem::File.stubs(:new).with(@basepath).returns @stub_file
end
describe "and generating files" do
@@ -160,7 +159,7 @@ describe tidy do
end
it "should do nothing if the targeted file does not exist" do
- File.expects(:lstat).with(@basepath).raises Errno::ENOENT
+ @stub_file.expects(:lstat).raises Errno::ENOENT
@tidy.generate.should == []
end
@@ -311,32 +310,33 @@ describe tidy do
before do
@tidy = Puppet::Type.type(:tidy).new :path => @basepath
@stat = stub 'stat', :ftype => "file"
- File.stubs(:lstat).with(@basepath).returns @stat
+ @stub_file = stub(@basepath, :lstat => @stat)
+ Puppet::FileSystem::File.expects(:new).with(@basepath).returns @stub_file
end
it "should not try to recurse if the file does not exist" do
@tidy[:recurse] = true
- File.stubs(:lstat).with(@basepath).returns nil
+ @stub_file.stubs(:lstat).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
+ @stub_file.expects(:lstat).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
+ @stub_file.expects(:lstat).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
+ @stub_file.expects(:lstat).returns stat
@tidy.should_not be_tidy(@basepath)
end
diff --git a/spec/unit/type/user_spec.rb b/spec/unit/type/user_spec.rb
index dd3dca6d9..2877d25c5 100755
--- a/spec/unit/type/user_spec.rb
+++ b/spec/unit/type/user_spec.rb
@@ -339,6 +339,15 @@ describe Puppet::Type.type(:user) do
end
end
+ describe "when managing comment on Ruby 1.9", :if => String.respond_to?(:encode) do
+ it "should force value encoding to ASCII-8BIT" do
+ value = 'abcd'.encode(Encoding::UTF_8)
+ comment = described_class.new(:name => 'foo', :comment => value)
+ comment[:comment].should == 'abcd'
+ comment[:comment].encoding.should == Encoding::ASCII_8BIT
+ end
+ end
+
describe "when manages_solaris_rbac is enabled" do
it "should support a :role value for ensure" do
expect { described_class.new(:name => 'foo', :ensure => :role) }.to_not raise_error
diff --git a/spec/unit/type_spec.rb b/spec/unit/type_spec.rb
index b160c743f..ca29d680e 100755
--- a/spec/unit/type_spec.rb
+++ b/spec/unit/type_spec.rb
@@ -1,9 +1,10 @@
#! /usr/bin/env ruby
require 'spec_helper'
-
+require 'puppet_spec/compiler'
describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
include PuppetSpec::Files
+ include PuppetSpec::Compiler
it "should be Comparable" do
a = Puppet::Type.type(:notify).new(:name => "a")
@@ -63,6 +64,28 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
end
end
+ it "can retrieve all set parameters" do
+ resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present, :tag => 'foo')
+ params = resource.parameters_with_value
+ [:name, :provider, :ensure, :fstype, :pass, :dump, :target, :loglevel, :tag].each do |name|
+ params.should be_include(resource.parameter(name))
+ end
+ end
+
+ it "can not return any `nil` values when retrieving all set parameters" do
+ resource = Puppet::Type.type(:mount).new(:name => "foo", :fstype => "bar", :pass => 1, :ensure => :present, :tag => 'foo')
+ params = resource.parameters_with_value
+ params.should_not be_include(nil)
+ end
+
+ it "can return an iterator for all set parameters" do
+ resource = Puppet::Type.type(:notify).new(:name=>'foo',:message=>'bar',:tag=>'baz',:require=> "File['foo']")
+ params = [:name, :message, :withpath, :loglevel, :tag, :require]
+ resource.eachparameter { |param|
+ params.should be_include(param.to_s.to_sym)
+ }
+ end
+
it "should have a method for setting default values for resources" do
Puppet::Type.type(:mount).new(:name => "foo").must respond_to(:set_default)
end
@@ -109,6 +132,36 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
Puppet::Type.type(:mount).new(:name => "foo").version.should == 0
end
+ it "reports the correct path even after path is used during setup of the type" do
+ Puppet::Type.newtype(:testing) do
+ newparam(:name) do
+ isnamevar
+ validate do |value|
+ path # forces the computation of the path
+ end
+ end
+ end
+
+ ral = compile_to_ral(<<-MANIFEST)
+ class something {
+ testing { something: }
+ }
+ include something
+ MANIFEST
+
+ ral.resource("Testing[something]").path.should == "/Stage[main]/Something/Testing[something]"
+ end
+
+ context "alias metaparam" do
+ it "creates a new name that can be used for resource references" do
+ ral = compile_to_ral(<<-MANIFEST)
+ notify { a: alias => c }
+ MANIFEST
+
+ expect(ral.resource("Notify[a]")).to eq(ral.resource("Notify[c]"))
+ end
+ end
+
context "resource attributes" do
let(:resource) {
resource = Puppet::Type.type(:mount).new(:name => "foo")
@@ -123,7 +176,8 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
end
it "should have tags" do
- resource.tags.should == ["mount", "foo"]
+ expect(resource).to be_tagged("mount")
+ expect(resource).to be_tagged("foo")
end
it "should have a path" do
@@ -165,13 +219,19 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
@resource.event.default_log_level.should == :warning
end
- {:file => "/my/file", :line => 50, :tags => %{foo bar}}.each do |attr, value|
+ {:file => "/my/file", :line => 50}.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 set the tags" do
+ @resource.tag("abc", "def")
+ @resource.event.should be_tagged("abc")
+ @resource.event.should be_tagged("def")
+ end
+
it "should allow specification of event attributes" do
@resource.event(:status => "noop").status.should == "noop"
end
@@ -470,6 +530,28 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
end
end
+ describe "when #finish is called on a type" do
+ let(:post_hook_type) do
+ Puppet::Type.newtype(:finish_test) do
+ newparam(:name) { isnamevar }
+
+ newparam(:post) do
+ def post_compile
+ raise "post_compile hook ran"
+ end
+ end
+ end
+ end
+
+ let(:post_hook_resource) do
+ post_hook_type.new(:name => 'foo',:post => 'fake_value')
+ end
+
+ it "should call #post_compile on parameters that implement it" do
+ expect { post_hook_resource.finish }.to raise_error(RuntimeError, "post_compile hook ran")
+ 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
@@ -588,7 +670,7 @@ describe Puppet::Type, :unless => Puppet.features.microsoft_windows? do
resource.should be_a Puppet::Resource
resource[:fstype].should == 15
resource[:remounts].should == :true
- resource.tags.should =~ %w{foo bar baz mount}
+ resource.tags.should == Puppet::Util::TagSet.new(%w{foo bar baz mount})
end
end
diff --git a/spec/unit/util/adsi_spec.rb b/spec/unit/util/adsi_spec.rb
index 3b851f5f1..974aba79c 100755
--- a/spec/unit/util/adsi_spec.rb
+++ b/spec/unit/util/adsi_spec.rb
@@ -52,13 +52,48 @@ describe Puppet::Util::ADSI do
end
end
+ describe ".sid_uri", :if => Puppet.features.microsoft_windows? do
+ it "should raise an error when the input is not a SID object" do
+ [Object.new, {}, 1, :symbol, '', nil].each do |input|
+ expect {
+ Puppet::Util::ADSI.sid_uri(input)
+ }.to raise_error(Puppet::Error, /Must use a valid SID object/)
+ end
+ end
+
+ it "should return a SID uri for a well-known SID (SYSTEM)" do
+ sid = Win32::Security::SID.new('SYSTEM')
+ Puppet::Util::ADSI.sid_uri(sid).should == 'WinNT://S-1-5-18'
+ end
+ end
+
describe Puppet::Util::ADSI::User do
let(:username) { 'testuser' }
+ let(:domain) { 'DOMAIN' }
+ let(:domain_username) { "#{domain}\\#{username}"}
it "should generate the correct URI" do
Puppet::Util::ADSI::User.uri(username).should == "WinNT://./#{username},user"
end
+ it "should generate the correct URI for a user with a domain" do
+ Puppet::Util::ADSI::User.uri(username, domain).should == "WinNT://#{domain}/#{username},user"
+ end
+
+ it "should be able to parse a username without a domain" do
+ Puppet::Util::ADSI::User.parse_name(username).should == [username, '.']
+ end
+
+ it "should be able to parse a username with a domain" do
+ Puppet::Util::ADSI::User.parse_name(domain_username).should == [username, domain]
+ end
+
+ it "should raise an error with a username that contains a /" do
+ expect {
+ Puppet::Util::ADSI::User.parse_name("#{domain}/#{username}")
+ }.to raise_error(Puppet::Error, /Value must be in DOMAIN\\user style syntax/)
+ end
+
it "should be able to create a user" do
adsi_user = stub('adsi')
@@ -76,6 +111,11 @@ describe Puppet::Util::ADSI do
Puppet::Util::ADSI::User.exists?(username).should be_true
end
+ it "should be able to check the existence of a domain user" do
+ Puppet::Util::ADSI.expects(:connect).with("WinNT://#{domain}/#{username},user").returns connection
+ Puppet::Util::ADSI::User.exists?(domain_username).should be_true
+ end
+
it "should be able to delete a user" do
connection.expects(:Delete).with('user', username)
@@ -85,7 +125,7 @@ describe Puppet::Util::ADSI do
it "should return an enumeration of IADsUser wrapped objects" do
name = 'Administrator'
wmi_users = [stub('WMI', :name => name)]
- Puppet::Util::ADSI.expects(:execquery).with("select name from win32_useraccount").returns(wmi_users)
+ Puppet::Util::ADSI.expects(:execquery).with('select name from win32_useraccount where localaccount = "TRUE"').returns(wmi_users)
native_user = stub('IADsUser')
homedir = "C:\\Users\\#{name}"
@@ -99,7 +139,8 @@ describe Puppet::Util::ADSI do
end
describe "an instance" do
- let(:adsi_user) { stub 'user' }
+ let(:adsi_user) { stub('user', :objectSID => []) }
+ let(:sid) { stub(:account => username, :domain => 'testcomputername') }
let(:user) { Puppet::Util::ADSI::User.new(username, adsi_user) }
it "should provide its groups as a list of names" do
@@ -133,14 +174,16 @@ describe Puppet::Util::ADSI do
user.password = 'pwd'
end
- it "should generate the correct URI" do
- user.uri.should == "WinNT://./#{username},user"
+ it "should generate the correct URI",:if => Puppet.features.microsoft_windows? do
+ Puppet::Util::Windows::Security.stubs(:octet_string_to_sid_object).returns(sid)
+ user.uri.should == "WinNT://testcomputername/#{username},user"
end
- describe "when given a set of groups to which to add the user" do
+ describe "when given a set of groups to which to add the user", :if => Puppet.features.microsoft_windows? do
let(:groups_to_set) { 'group1,group2' }
before(:each) do
+ Puppet::Util::Windows::Security.stubs(:octet_string_to_sid_object).returns(sid)
user.expects(:groups).returns ['group2', 'group3']
end
@@ -152,6 +195,7 @@ describe Puppet::Util::ADSI do
group3 = stub 'group1'
group3.expects(:Remove).with("WinNT://testcomputername/#{username},user")
+ Puppet::Util::ADSI.expects(:sid_uri).with(sid).returns("WinNT://testcomputername/#{username},user").twice
Puppet::Util::ADSI.expects(:connect).with('WinNT://./group1,group').returns group1
Puppet::Util::ADSI.expects(:connect).with('WinNT://./group3,group').returns group3
@@ -164,6 +208,7 @@ describe Puppet::Util::ADSI do
group1 = stub 'group1'
group1.expects(:Add).with("WinNT://testcomputername/#{username},user")
+ Puppet::Util::ADSI.expects(:sid_uri).with(sid).returns("WinNT://testcomputername/#{username},user")
Puppet::Util::ADSI.expects(:connect).with('WinNT://./group1,group').returns group1
user.set_groups(groups_to_set, true)
@@ -179,19 +224,58 @@ describe Puppet::Util::ADSI do
describe "an instance" do
let(:adsi_group) { stub 'group' }
let(:group) { Puppet::Util::ADSI::Group.new(groupname, adsi_group) }
+ let(:someone_sid){ stub(:account => 'someone', :domain => 'testcomputername')}
+
+ it "should be able to add a member (deprecated)", :if => Puppet.features.microsoft_windows? do
+ Puppet.expects(:deprecation_warning).with('Puppet::Util::ADSI::Group#add_members is deprecated; please use Puppet::Util::ADSI::Group#add_member_sids')
+
+ Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('someone').returns(someone_sid)
+ Puppet::Util::ADSI.expects(:sid_uri).with(someone_sid).returns("WinNT://testcomputername/someone,user")
- it "should be able to add a member" do
adsi_group.expects(:Add).with("WinNT://testcomputername/someone,user")
group.add_member('someone')
end
- it "should be able to remove a member" do
+ it "should raise when adding a member that can't resolve to a SID (deprecated)", :if => Puppet.features.microsoft_windows? do
+ expect {
+ group.add_member('foobar')
+ }.to raise_error(Puppet::Error, /Could not resolve username: foobar/)
+ end
+
+ it "should be able to remove a member (deprecated)", :if => Puppet.features.microsoft_windows? do
+ Puppet.expects(:deprecation_warning).with('Puppet::Util::ADSI::Group#remove_members is deprecated; please use Puppet::Util::ADSI::Group#remove_member_sids')
+
+ Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('someone').returns(someone_sid)
+ Puppet::Util::ADSI.expects(:sid_uri).with(someone_sid).returns("WinNT://testcomputername/someone,user")
+
adsi_group.expects(:Remove).with("WinNT://testcomputername/someone,user")
group.remove_member('someone')
end
+ it "should raise when removing a member that can't resolve to a SID (deprecated)", :if => Puppet.features.microsoft_windows? do
+ expect {
+ group.remove_member('foobar')
+ }.to raise_error(Puppet::Error, /Could not resolve username: foobar/)
+ end
+
+ describe "should be able to use SID objects", :if => Puppet.features.microsoft_windows? do
+ let(:system) { Puppet::Util::Windows::Security.name_to_sid_object('SYSTEM') }
+
+ it "to add a member" do
+ adsi_group.expects(:Add).with("WinNT://S-1-5-18")
+
+ group.add_member_sids(system)
+ end
+
+ it "to remove a member" do
+ adsi_group.expects(:Remove).with("WinNT://S-1-5-18")
+
+ group.remove_member_sids(system)
+ end
+ end
+
it "should provide its groups as a list of names" do
names = ['user1', 'user2']
@@ -202,14 +286,38 @@ describe Puppet::Util::ADSI do
group.members.should =~ names
end
- it "should be able to add a list of users to a group" do
- names = ['user1', 'user2']
- adsi_group.expects(:Members).returns names.map{|n| stub(:Name => n)}
+ it "should be able to add a list of users to a group", :if => Puppet.features.microsoft_windows? do
+ names = ['DOMAIN\user1', 'user2']
+ sids = [
+ stub(:account => 'user1', :domain => 'DOMAIN'),
+ stub(:account => 'user2', :domain => 'testcomputername'),
+ stub(:account => 'user3', :domain => 'DOMAIN2'),
+ ]
+
+ # use stubbed objectSid on member to return stubbed SID
+ Puppet::Util::Windows::Security.expects(:octet_string_to_sid_object).with([0]).returns(sids[0])
+ Puppet::Util::Windows::Security.expects(:octet_string_to_sid_object).with([1]).returns(sids[1])
- adsi_group.expects(:Remove).with('WinNT://testcomputername/user1,user')
- adsi_group.expects(:Add).with('WinNT://testcomputername/user3,user')
+ Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('user2').returns(sids[1])
+ Puppet::Util::Windows::Security.expects(:name_to_sid_object).with('DOMAIN2\user3').returns(sids[2])
+
+ Puppet::Util::ADSI.expects(:sid_uri).with(sids[0]).returns("WinNT://DOMAIN/user1,user")
+ Puppet::Util::ADSI.expects(:sid_uri).with(sids[2]).returns("WinNT://DOMAIN2/user3,user")
+
+ members = names.each_with_index.map{|n,i| stub(:Name => n, :objectSID => [i])}
+ adsi_group.expects(:Members).returns members
+
+ adsi_group.expects(:Remove).with('WinNT://DOMAIN/user1,user')
+ adsi_group.expects(:Add).with('WinNT://DOMAIN2/user3,user')
+
+ group.set_members(['user2', 'DOMAIN2\user3'])
+ end
- group.set_members(['user2', 'user3'])
+ it "should raise an error when a username does not resolve to a SID", :if => Puppet.features.microsoft_windows? do
+ expect {
+ adsi_group.expects(:Members).returns []
+ group.set_members(['foobar'])
+ }.to raise_error(Puppet::Error, /Could not resolve username: foobar/)
end
it "should generate the correct URI" do
@@ -248,7 +356,7 @@ describe Puppet::Util::ADSI do
it "should return an enumeration of IADsGroup wrapped objects" do
name = 'Administrators'
wmi_groups = [stub('WMI', :name => name)]
- Puppet::Util::ADSI.expects(:execquery).with("select name from win32_group").returns(wmi_groups)
+ Puppet::Util::ADSI.expects(:execquery).with('select name from win32_group where localaccount = "TRUE"').returns(wmi_groups)
native_group = stub('IADsGroup')
native_group.expects(:Members).returns([stub(:Name => 'Administrator')])
diff --git a/spec/unit/util/autoload_spec.rb b/spec/unit/util/autoload_spec.rb
index 933855914..4a0782ac7 100755
--- a/spec/unit/util/autoload_spec.rb
+++ b/spec/unit/util/autoload_spec.rb
@@ -71,7 +71,7 @@ describe Puppet::Util::Autoload do
[RuntimeError, LoadError, SyntaxError].each do |error|
it "should die with Puppet::Error if a #{error.to_s} exception is thrown" do
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Kernel.expects(:load).raises error
@@ -84,7 +84,7 @@ describe Puppet::Util::Autoload do
end
it "should register loaded files with the autoloader" do
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Kernel.stubs(:load)
@autoload.load("myfile")
@@ -94,7 +94,7 @@ describe Puppet::Util::Autoload do
end
it "should be seen by loaded? on the instance using the short name" do
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Kernel.stubs(:load)
@autoload.load("myfile")
@@ -104,7 +104,7 @@ describe Puppet::Util::Autoload do
end
it "should register loaded files with the main loaded file list so they are not reloaded by ruby" do
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Kernel.stubs(:load)
@autoload.load("myfile")
@@ -117,7 +117,7 @@ describe Puppet::Util::Autoload do
it "should load the first file in the searchpath" do
@autoload.stubs(:search_directories).returns [make_absolute("/a"), make_absolute("/b")]
FileTest.stubs(:directory?).returns true
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Kernel.expects(:load).with(make_absolute("/a/tmp/myfile.rb"), optionally(anything))
@autoload.load("myfile")
@@ -126,7 +126,7 @@ describe Puppet::Util::Autoload do
end
it "should treat equivalent paths to a loaded file as loaded" do
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Kernel.stubs(:load)
@autoload.load("myfile")
@@ -144,7 +144,7 @@ describe Puppet::Util::Autoload do
@autoload.class.stubs(:search_directories).returns [make_absolute("/a")]
FileTest.stubs(:directory?).returns true
Dir.stubs(:glob).returns [make_absolute("/a/foo/file.rb")]
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
@time_a = Time.utc(2010, 'jan', 1, 6, 30)
File.stubs(:mtime).returns @time_a
@@ -185,7 +185,7 @@ describe Puppet::Util::Autoload do
it "changes should be seen by changed? on the instance using the short name" do
File.stubs(:mtime).returns(@first_time)
- File.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
Kernel.stubs(:load)
@autoload.load("myfile")
@autoload.loaded?("myfile").should be
@@ -206,14 +206,14 @@ describe Puppet::Util::Autoload do
it "should reload if mtime changes" do
File.stubs(:mtime).with(@file_a).returns(@first_time + 60)
- File.stubs(:exist?).with(@file_a).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns true
Kernel.expects(:load).with(@file_a, optionally(anything))
@autoload.class.reload_changed
end
it "should do nothing if the file is deleted" do
File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT)
- File.stubs(:exist?).with(@file_a).returns false
+ Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns false
Kernel.expects(:load).never
@autoload.class.reload_changed
end
@@ -228,8 +228,8 @@ describe Puppet::Util::Autoload do
File.expects(:mtime).with(@file_a).returns(@first_time)
@autoload.class.mark_loaded("file", @file_a)
File.stubs(:mtime).with(@file_a).raises(Errno::ENOENT)
- File.stubs(:exist?).with(@file_a).returns false
- File.stubs(:exist?).with(@file_b).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns false
+ Puppet::FileSystem::File.stubs(:exist?).with(@file_b).returns true
File.stubs(:mtime).with(@file_b).returns @first_time
Kernel.expects(:load).with(@file_b, optionally(anything))
@autoload.class.reload_changed
@@ -238,11 +238,11 @@ describe Puppet::Util::Autoload do
it "should load a/file when b/file is loaded and a/file is created" do
File.stubs(:mtime).with(@file_b).returns @first_time
- File.stubs(:exist?).with(@file_b).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(@file_b).returns true
@autoload.class.mark_loaded("file", @file_b)
File.stubs(:mtime).with(@file_a).returns @first_time
- File.stubs(:exist?).with(@file_a).returns true
+ Puppet::FileSystem::File.stubs(:exist?).with(@file_a).returns true
Kernel.expects(:load).with(@file_a, optionally(anything))
@autoload.class.reload_changed
@autoload.class.send(:loaded)["file"].should == [@file_a, @first_time]
diff --git a/spec/unit/util/backups_spec.rb b/spec/unit/util/backups_spec.rb
index 8d3ea1956..654ddb788 100755
--- a/spec/unit/util/backups_spec.rb
+++ b/spec/unit/util/backups_spec.rb
@@ -20,7 +20,7 @@ describe Puppet::Util::Backups do
file = Puppet::Type.type(:file).new(:name => path)
file.expects(:bucket).never
- FileTest.expects(:exists?).with(path).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns false
file.perform_backup
end
@@ -29,23 +29,25 @@ describe Puppet::Util::Backups do
file = Puppet::Type.type(:file).new(:name => path, :backup => false)
file.expects(:bucket).never
- FileTest.expects(:exists?).never
+ Puppet::FileSystem::File.expects(:exist?).never
file.perform_backup
end
it "a bucket should be used when provided" do
- File.stubs(:lstat).with(path).returns(mock('lstat', :ftype => 'file'))
+ stub_file = stub(path, :lstat => mock('lstat', :ftype => 'file'))
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
bucket.expects(:backup).with(path).returns("mysum")
- FileTest.expects(:exists?).with(path).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns(true)
file.perform_backup
end
it "should propagate any exceptions encountered when backing up to a filebucket" do
- File.stubs(:lstat).with(path).returns(mock('lstat', :ftype => 'file'))
+ stub_file = stub(path, :lstat => mock('lstat', :ftype => 'file'))
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
bucket.expects(:backup).raises ArgumentError
- FileTest.expects(:exists?).with(path).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns(true)
lambda { file.perform_backup }.should raise_error(ArgumentError)
end
@@ -56,35 +58,39 @@ describe Puppet::Util::Backups do
let(:file) { Puppet::Type.type(:file).new(:name => path, :backup => '.'+ext) }
it "should remove any local backup if one exists" do
- File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file")
- File.expects(:unlink).with(backup)
+ stub_file = stub(backup, :lstat => stub('stat', :ftype => 'file'))
+ Puppet::FileSystem::File.expects(:new).with(backup).returns stub_file
+ Puppet::FileSystem::File.expects(:unlink).with(backup)
FileUtils.stubs(:cp_r)
- FileTest.expects(:exists?).with(path).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns(true)
file.perform_backup
end
it "should fail when the old backup can't be removed" do
- File.expects(:lstat).with(backup).returns stub("stat", :ftype => "file")
- File.expects(:unlink).with(backup).raises ArgumentError
+ stub_file = stub(backup, :lstat => stub('stat', :ftype => 'file'))
+ Puppet::FileSystem::File.expects(:new).with(backup).returns stub_file
+ Puppet::FileSystem::File.expects(:unlink).with(backup).raises ArgumentError
FileUtils.expects(:cp_r).never
- FileTest.expects(:exists?).with(path).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns(true)
lambda { file.perform_backup }.should raise_error(Puppet::Error)
end
it "should not try to remove backups that don't exist" do
- File.expects(:lstat).with(backup).raises(Errno::ENOENT)
- File.expects(:unlink).with(backup).never
+ stub_file = stub(backup)
+ Puppet::FileSystem::File.expects(:new).with(backup).returns stub_file
+ stub_file.expects(:lstat).raises(Errno::ENOENT)
+ Puppet::FileSystem::File.expects(:unlink).with(backup).never
FileUtils.stubs(:cp_r)
- FileTest.expects(:exists?).with(path).returns(true)
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns(true)
file.perform_backup
end
it "a copy should be created in the local directory" do
FileUtils.expects(:cp_r).with(path, backup, :preserve => true)
- FileTest.stubs(:exists?).with(path).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(path).returns(true)
file.perform_backup.should be_true
end
@@ -92,7 +98,7 @@ describe Puppet::Util::Backups do
it "should propagate exceptions if no backup can be created" do
FileUtils.expects(:cp_r).raises ArgumentError
- FileTest.stubs(:exists?).with(path).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(path).returns(true)
lambda { file.perform_backup }.should raise_error(Puppet::Error)
end
end
@@ -108,10 +114,11 @@ describe Puppet::Util::Backups do
bucket.expects(:backup).with(filename).returns true
- File.stubs(:lstat).with(path).returns(stub('lstat', :ftype => 'directory'))
+ stub_file = stub(path, :lstat => stub('stat', :ftype => 'directory'))
+ Puppet::FileSystem::File.expects(:new).with(path).returns stub_file
- FileTest.stubs(:exists?).with(path).returns(true)
- FileTest.stubs(:exists?).with(filename).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(path).returns(true)
+ Puppet::FileSystem::File.stubs(:exist?).with(filename).returns(true)
file.perform_backup
end
@@ -120,7 +127,8 @@ describe Puppet::Util::Backups do
file = Puppet::Type.type(:file).new(:name => path, :backup => 'foo', :recurse => true)
bucket.expects(:backup).never
- File.stubs(:stat).with(path).returns(stub('stat', :ftype => 'directory'))
+ stub_file = stub('file', :stat => stub('stat', :ftype => 'directory'))
+ Puppet::FileSystem::File.stubs(:new).with(path).returns stub_file
Find.expects(:find).never
file.perform_backup
diff --git a/spec/unit/util/checksums_spec.rb b/spec/unit/util/checksums_spec.rb
index e928ca8ae..0adff7a9a 100755
--- a/spec/unit/util/checksums_spec.rb
+++ b/spec/unit/util/checksums_spec.rb
@@ -132,7 +132,8 @@ describe Puppet::Util::Checksums do
file = "/my/file"
stat = mock 'stat', sum => "mysum"
- File.expects(:stat).with(file).returns(stat)
+ stub_file = stub(file, :stat => stat)
+ Puppet::FileSystem::File.expects(:new).with(file).returns stub_file
@summer.send(sum.to_s + "_file", file).should == "mysum"
end
diff --git a/spec/unit/util/command_line_spec.rb b/spec/unit/util/command_line_spec.rb
index 3d8f896d1..6ba8077c2 100755
--- a/spec/unit/util/command_line_spec.rb
+++ b/spec/unit/util/command_line_spec.rb
@@ -143,5 +143,46 @@ describe Puppet::Util::CommandLine do
Puppet::Util::CommandLine.available_subcommands
end
end
+
+ describe 'when setting process priority' do
+ let(:command_line) do
+ Puppet::Util::CommandLine.new("puppet", %w{ agent })
+ end
+
+ before :each do
+ Puppet::Util::CommandLine::ApplicationSubcommand.any_instance.stubs(:run)
+ end
+
+ it 'should never set priority by default' do
+ Process.expects(:setpriority).never
+
+ command_line.execute
+ end
+
+ it 'should lower the process priority if one has been specified' do
+ Puppet[:priority] = 10
+
+ Process.expects(:setpriority).with(0, Process.pid, 10)
+ command_line.execute
+ end
+
+ it 'should warn if trying to raise priority, but not privileged user' do
+ Puppet[:priority] = -10
+
+ Process.expects(:setpriority).raises(Errno::EACCES, 'Permission denied')
+ Puppet.expects(:warning).with("Failed to set process priority to '-10'")
+
+ command_line.execute
+ end
+
+ it "should warn if the platform doesn't support `Process.setpriority`" do
+ Puppet[:priority] = 15
+
+ Process.expects(:setpriority).raises(NotImplementedError, 'NotImplementedError: setpriority() function is unimplemented on this machine')
+ Puppet.expects(:warning).with("Failed to set process priority to '15'")
+
+ command_line.execute
+ end
+ end
end
end
diff --git a/spec/unit/util/docs_spec.rb b/spec/unit/util/docs_spec.rb
new file mode 100644
index 000000000..eb736ff72
--- /dev/null
+++ b/spec/unit/util/docs_spec.rb
@@ -0,0 +1,91 @@
+require 'spec_helper'
+
+describe Puppet::Util::Docs do
+
+ describe '.scrub' do
+ let(:my_cleaned_output) do
+ %q{This resource type uses the prescribed native tools for creating
+groups and generally uses POSIX APIs for retrieving information
+about them. It does not directly modify `/etc/passwd` or anything.
+
+* Just for fun, we'll add a list.
+* list item two,
+ which has some add'l lines included in it.
+
+And here's a code block:
+
+ this is the piece of code
+ it does something cool
+
+**Autorequires:** I would be listing autorequired resources here.}
+ end
+
+ it "strips the least common indent from multi-line strings, without mangling indentation beyond the least common indent" do
+ input = <<EOT
+ This resource type uses the prescribed native tools for creating
+ groups and generally uses POSIX APIs for retrieving information
+ about them. It does not directly modify `/etc/passwd` or anything.
+
+ * Just for fun, we'll add a list.
+ * list item two,
+ which has some add'l lines included in it.
+
+ And here's a code block:
+
+ this is the piece of code
+ it does something cool
+
+ **Autorequires:** I would be listing autorequired resources here.
+EOT
+ output = Puppet::Util::Docs.scrub(input)
+ expect(output).to eq my_cleaned_output
+ end
+
+ it "ignores the first line when calculating least common indent" do
+ input = "This resource type uses the prescribed native tools for creating
+ groups and generally uses POSIX APIs for retrieving information
+ about them. It does not directly modify `/etc/passwd` or anything.
+
+ * Just for fun, we'll add a list.
+ * list item two,
+ which has some add'l lines included in it.
+
+ And here's a code block:
+
+ this is the piece of code
+ it does something cool
+
+ **Autorequires:** I would be listing autorequired resources here."
+ output = Puppet::Util::Docs.scrub(input)
+ expect(output).to eq my_cleaned_output
+ end
+
+ it "strips trailing whitespace from each line, and strips trailing newlines at end" do
+ input = "This resource type uses the prescribed native tools for creating \n groups and generally uses POSIX APIs for retrieving information \n about them. It does not directly modify `/etc/passwd` or anything. \n\n * Just for fun, we'll add a list. \n * list item two,\n which has some add'l lines included in it. \n\n And here's a code block:\n\n this is the piece of code \n it does something cool \n\n **Autorequires:** I would be listing autorequired resources here. \n\n"
+ output = Puppet::Util::Docs.scrub(input)
+ expect(output).to eq my_cleaned_output
+ end
+
+ it "has no side effects on original input string" do
+ input = "First line \n second line \n \n indented line \n \n last line\n\n"
+ clean_input = "First line \n second line \n \n indented line \n \n last line\n\n"
+ not_used = Puppet::Util::Docs.scrub(input)
+ expect(input).to eq clean_input
+ end
+
+ it "does not include whitespace-only lines when calculating least common indent" do
+ input = "First line\n second line\n \n indented line\n\n last line"
+ expected_output = "First line\nsecond line\n\n indented line\n\nlast line"
+ #bogus_output = "First line\nsecond line\n\n indented line\n\nlast line"
+ output = Puppet::Util::Docs.scrub(input)
+ expect(output).to eq expected_output
+ end
+
+ it "accepts a least common indent of zero, thus not adding errors when input string is already scrubbed" do
+ expect(Puppet::Util::Docs.scrub(my_cleaned_output)).to eq my_cleaned_output
+ end
+
+
+ end
+end
+
diff --git a/spec/unit/util/execution_spec.rb b/spec/unit/util/execution_spec.rb
index 71af8f01f..7bb15cd75 100755
--- a/spec/unit/util/execution_spec.rb
+++ b/spec/unit/util/execution_spec.rb
@@ -306,11 +306,21 @@ describe Puppet::Util::Execution do
Puppet::Util::Execution.stubs(:execute_windows).returns(proc_info_stub)
Puppet::Util::Windows::Process.expects(:wait_process).with(process_handle).raises('whatever')
- Process.expects(:CloseHandle).with(thread_handle)
- Process.expects(:CloseHandle).with(process_handle)
+ Puppet::Util::Windows::Process.expects(:CloseHandle).with(thread_handle)
+ Puppet::Util::Windows::Process.expects(:CloseHandle).with(process_handle)
expect { Puppet::Util::Execution.execute('test command') }.to raise_error(RuntimeError)
end
+
+ it "should return the correct exit status even when exit status is greater than 256" do
+ real_exit_status = 3010
+
+ Puppet::Util::Execution.stubs(:execute_windows).returns(proc_info_stub)
+ stub_process_wait(real_exit_status)
+ $CHILD_STATUS.stubs(:exitstatus).returns(real_exit_status % 256) # The exitstatus is changed to be mod 256 so that ruby can fit it into 8 bits.
+
+ Puppet::Util::Execution.execute('test command', :failonfail => false).exitstatus.should == real_exit_status
+ end
end
end
@@ -509,7 +519,7 @@ describe Puppet::Util::Execution do
Tempfile.stubs(:new).returns(stdout)
stdout.write("My expected command output")
- Puppet::Util::Execution.execute('test command', :squelch => true).should == nil
+ Puppet::Util::Execution.execute('test command', :squelch => true).should == ''
end
it "should delete the file used for output if squelch is false" do
@@ -519,7 +529,7 @@ describe Puppet::Util::Execution do
Puppet::Util::Execution.execute('test command')
- File.should_not be_exist(path)
+ Puppet::FileSystem::File.exist?(path).should be_false
end
it "should not raise an error if the file is open" do
@@ -591,6 +601,20 @@ describe Puppet::Util::Execution do
Puppet::Util::Execution.execpipe('echo hello').should == 'hello'
end
+ it "should print meaningful debug message for string argument" do
+ Puppet::Util::Execution.expects(:debug).with("Executing 'echo hello'")
+ Puppet::Util::Execution.expects(:open).with('| echo hello 2>&1').returns('hello')
+ $CHILD_STATUS.expects(:==).with(0).returns(true)
+ Puppet::Util::Execution.execpipe('echo hello')
+ end
+
+ it "should print meaningful debug message for array argument" do
+ Puppet::Util::Execution.expects(:debug).with("Executing 'echo hello'")
+ Puppet::Util::Execution.expects(:open).with('| echo hello 2>&1').returns('hello')
+ $CHILD_STATUS.expects(:==).with(0).returns(true)
+ Puppet::Util::Execution.execpipe(['echo','hello'])
+ end
+
it "should execute an array by pasting together with spaces" do
Puppet::Util::Execution.expects(:open).with('| echo hello 2>&1').returns('hello')
$CHILD_STATUS.expects(:==).with(0).returns(true)
diff --git a/spec/unit/util/filetype_spec.rb b/spec/unit/util/filetype_spec.rb
index ecf7c3690..5d8f0b36d 100755
--- a/spec/unit/util/filetype_spec.rb
+++ b/spec/unit/util/filetype_spec.rb
@@ -16,15 +16,15 @@ describe Puppet::Util::FileType do
describe "when the file already exists" do
it "should return the file's contents when asked to read it" do
- File.expects(:exist?).with(path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns true
File.expects(:read).with(path).returns "my text"
file.read.should == "my text"
end
it "should unlink the file when asked to remove it" do
- File.expects(:exist?).with(path).returns true
- File.expects(:unlink).with(path)
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns true
+ Puppet::FileSystem::File.expects(:unlink).with(path)
file.remove
end
@@ -32,7 +32,7 @@ describe Puppet::Util::FileType do
describe "when the file does not exist" do
it "should return an empty string when asked to read the file" do
- File.expects(:exist?).with(path).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns false
file.read.should == ""
end
@@ -63,13 +63,13 @@ describe Puppet::Util::FileType do
describe "when backing up a file" do
it "should do nothing if the file does not exist" do
- File.expects(:exists?).with(path).returns false
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns false
file.expects(:bucket).never
file.backup
end
it "should use its filebucket to backup the file if it exists" do
- File.expects(:exists?).with(path).returns true
+ Puppet::FileSystem::File.expects(:exist?).with(path).returns true
bucket = mock 'bucket'
bucket.expects(:backup).with(path)
@@ -155,7 +155,7 @@ describe Puppet::Util::FileType do
end
after :each do
- File.should_not be_exist @tmp_cron_path
+ Puppet::FileSystem::File.exist?(@tmp_cron_path).should be_false
end
it "should run crontab as the target user on a temporary file" do
diff --git a/spec/unit/util/lockfile_spec.rb b/spec/unit/util/lockfile_spec.rb
index 4f3463429..8d10d5106 100644
--- a/spec/unit/util/lockfile_spec.rb
+++ b/spec/unit/util/lockfile_spec.rb
@@ -25,7 +25,7 @@ describe Puppet::Util::Lockfile do
it "should create a lock file" do
@lock.lock
- File.should be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_true
end
it "should create a lock file containing a string" do
@@ -49,7 +49,7 @@ describe Puppet::Util::Lockfile do
it "should clear the lock file" do
File.open(@lockfile, 'w') { |fd| fd.print("locked") }
@lock.unlock
- File.should_not be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_false
end
end
diff --git a/spec/unit/util/log/destinations_spec.rb b/spec/unit/util/log/destinations_spec.rb
index 332e8ad7c..b34b973cf 100755
--- a/spec/unit/util/log/destinations_spec.rb
+++ b/spec/unit/util/log/destinations_spec.rb
@@ -108,6 +108,38 @@ describe Puppet::Util::Log.desttypes[:syslog] do
end
end
+describe Puppet::Util::Log.desttypes[:logstash_event] do
+
+ describe "when using structured log format with logstash_event schema" do
+ before :each do
+ @msg = Puppet::Util::Log.new(:level => :info, :message => "So long, and thanks for all the fish.", :source => "a dolphin")
+ end
+
+ it "format should fix the hash to have the correct structure" do
+ dest = described_class.new
+ result = dest.format(@msg)
+ result["version"].should == 1
+ result["level"].should == :info
+ result["message"].should == "So long, and thanks for all the fish."
+ result["source"].should == "a dolphin"
+ # timestamp should be within 10 seconds
+ Time.parse(result["@timestamp"]).should >= ( Time.now - 10 )
+ end
+
+ it "format returns a structure that can be converted to json" do
+ dest = described_class.new
+ hash = dest.format(@msg)
+ JSON.parse(hash.to_json)
+ end
+
+ it "handle should send the output to stdout" do
+ $stdout.expects(:puts).once
+ dest = described_class.new
+ dest.handle(@msg)
+ end
+ end
+end
+
describe Puppet::Util::Log.desttypes[:console] do
let (:klass) { Puppet::Util::Log.desttypes[:console] }
diff --git a/spec/unit/util/monkey_patches_spec.rb b/spec/unit/util/monkey_patches_spec.rb
index cc487020c..13e10454c 100755
--- a/spec/unit/util/monkey_patches_spec.rb
+++ b/spec/unit/util/monkey_patches_spec.rb
@@ -280,6 +280,47 @@ describe OpenSSL::SSL::SSLContext do
end
end
+
+describe OpenSSL::X509::Store, :if => Puppet::Util::Platform.windows? do
+ let(:store) { described_class.new }
+ let(:cert) { OpenSSL::X509::Certificate.new(File.read(my_fixture('x509.pem'))) }
+
+ def with_root_certs(certs)
+ Puppet::Util::Windows::RootCerts.expects(:instance).returns(certs)
+ end
+
+ it "adds a root cert to the store" do
+ with_root_certs([cert])
+
+ store.set_default_paths
+ end
+
+ it "ignores duplicate root certs" do
+ with_root_certs([cert, cert])
+
+ store.expects(:add_cert).with(cert).once
+
+ store.set_default_paths
+ end
+
+ it "warns when adding a certificate that already exists" do
+ with_root_certs([cert])
+ store.add_cert(cert)
+
+ store.expects(:warn).with('Failed to add /DC=com/DC=microsoft/CN=Microsoft Root Certificate Authority')
+
+ store.set_default_paths
+ end
+
+ it "raises when adding an invalid certificate" do
+ with_root_certs(['notacert'])
+
+ expect {
+ store.set_default_paths
+ }.to raise_error(TypeError)
+ end
+end
+
describe SecureRandom do
it 'generates a properly formatted uuid' do
SecureRandom.uuid.should =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i
diff --git a/spec/unit/util/pidlock_spec.rb b/spec/unit/util/pidlock_spec.rb
index 3e70ce008..e9c28f2b0 100644
--- a/spec/unit/util/pidlock_spec.rb
+++ b/spec/unit/util/pidlock_spec.rb
@@ -47,7 +47,7 @@ describe Puppet::Util::Pidlock do
it "should create a lock file" do
@lock.lock
- File.should be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_true
end
it "should expose the lock file_path" do
@@ -74,7 +74,7 @@ describe Puppet::Util::Pidlock do
it "should get rid of the lock file" do
@lock.lock
@lock.unlock
- File.should_not be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_false
end
end
@@ -106,12 +106,12 @@ describe Puppet::Util::Pidlock do
describe "#lock" do
it "should clear stale locks" do
@lock.locked?
- File.should_not be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_false
end
it "should replace with new locks" do
@lock.lock
- File.should be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_true
@lock.lock_pid.should == 6789
@lock.should be_mine
@lock.should be_locked
@@ -125,7 +125,7 @@ describe Puppet::Util::Pidlock do
it "should not remove the lock file" do
@lock.unlock
- File.should be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_true
end
end
end
@@ -170,7 +170,7 @@ describe Puppet::Util::Pidlock do
it "should not remove the lock file" do
@lock.unlock
- File.should be_exists(@lockfile)
+ Puppet::FileSystem::File.exist?(@lockfile).should be_true
end
it "should still not be our lock" do
diff --git a/spec/unit/util/rdoc/parser_spec.rb b/spec/unit/util/rdoc/parser_spec.rb
index 476d15214..1699020a7 100755
--- a/spec/unit/util/rdoc/parser_spec.rb
+++ b/spec/unit/util/rdoc/parser_spec.rb
@@ -13,7 +13,9 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
include PuppetSpec::Files
before :each do
- File.stubs(:stat).with("init.pp")
+ stub_file = stub('init.pp', :stat => stub())
+ # Ruby 1.8.7 needs the following call to be stubs and not expects
+ Puppet::FileSystem::File.stubs(:new).with('init.pp').returns stub_file
@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
@@ -82,7 +84,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
File.stubs(:open).returns("readme")
@parser.stubs(:parse_elements)
- @module.expects(:comment=).with("readme")
+ @module.expects(:add_comment).with("readme", "module/manifests/init.pp")
@parser.scan_top_level(@topcontainer)
end
@@ -93,7 +95,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
File.stubs(:open).returns("readme")
@parser.stubs(:parse_elements)
- @module.expects(:comment=).with("readme")
+ @module.expects(:add_comment).with("readme", "module/manifests/init.pp")
@parser.scan_top_level(@topcontainer)
end
@@ -105,7 +107,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
File.stubs(:open).with("module/README.rdoc", "r").returns("readme.rdoc")
@parser.stubs(:parse_elements)
- @module.expects(:comment=).with("readme.rdoc")
+ @module.expects(:add_comment).with("readme.rdoc", "module/manifests/init.pp")
@parser.scan_top_level(@topcontainer)
end
@@ -311,7 +313,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
end
it "should associate the node documentation to the rdoc node" do
- @rdoc_node.expects(:comment=).with("mydoc")
+ @rdoc_node.expects(:add_comment).with("mydoc", "file")
@parser.document_node("mynode", @node, @class)
end
@@ -360,7 +362,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
end
it "should associate the node documentation to the rdoc class" do
- @rdoc_class.expects(:comment=).with("mydoc")
+ @rdoc_class.expects(:add_comment).with("mydoc", "file")
@parser.document_class("mynode", @class, @module)
end
@@ -397,7 +399,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
before(:each) do
@class = stub_everything 'class'
@code = stub_everything 'code'
- @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true)
+ @code.stubs(:is_a?).with(Puppet::Parser::AST::BlockExpression).returns(true)
end
it "should also scan mono-instruction code" do
@@ -435,7 +437,7 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
before(:each) do
@class = stub_everything 'class'
@code = stub_everything 'code'
- @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true)
+ @code.stubs(:is_a?).with(Puppet::Parser::AST::BlockExpression).returns(true)
end
it "should also scan mono-instruction code" do
@@ -457,11 +459,11 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? 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::BlockExpression).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)
+ @code.stubs(:is_a?).with(Puppet::Parser::AST::BlockExpression).returns(true)
end
it "should recursively register variables to the current container" do
@@ -483,17 +485,17 @@ describe "RDoc::Parser", :if => Puppet.features.rdoc1? do
@class = stub_everything 'class'
@stmt = Puppet::Parser::AST::Resource.new(
:type => "File",
- :instances => Puppet::Parser::AST::ASTArray.new(:children => [
+ :instances => Puppet::Parser::AST::BlockExpression.new(:children => [
Puppet::Parser::AST::ResourceInstance.new(
:title => Puppet::Parser::AST::Name.new(:value => "myfile"),
- :parameters => Puppet::Parser::AST::ASTArray.new(:children => [])
+ :parameters => Puppet::Parser::AST::BlockExpression.new(:children => [])
)
]),
:doc => 'mydoc'
)
@code = stub_everything 'code'
- @code.stubs(:is_a?).with(Puppet::Parser::AST::ASTArray).returns(true)
+ @code.stubs(:is_a?).with(Puppet::Parser::AST::BlockExpression).returns(true)
end
it "should register a PuppetResource to the current container" do
diff --git a/spec/unit/util/rdoc_spec.rb b/spec/unit/util/rdoc_spec.rb
index b13591374..f71bf7939 100755
--- a/spec/unit/util/rdoc_spec.rb
+++ b/spec/unit/util/rdoc_spec.rb
@@ -5,15 +5,7 @@ require 'puppet/util/rdoc'
require 'rdoc/rdoc'
describe Puppet::Util::RDoc do
- it "should fail with a clear error without RDoc 1.*" do
- Puppet.features.stubs(:rdoc1?).returns(false)
-
- expect {
- Puppet::Util::RDoc.rdoc("output", [])
- }.to raise_error(/the version of RDoc .* is not supported/)
- end
-
- describe "when generating RDoc HTML documentation", :if => Puppet.features.rdoc1? do
+ describe "when generating RDoc HTML documentation" do
before :each do
@rdoc = stub_everything 'rdoc'
RDoc::RDoc.stubs(:new).returns(@rdoc)
@@ -24,12 +16,6 @@ describe Puppet::Util::RDoc do
Puppet::Util::RDoc.rdoc("output", [])
end
- it "should install the Puppet HTML Generator into RDoc generators" do
- Puppet::Util::RDoc.rdoc("output", [])
-
- RDoc::RDoc::GENERATORS["puppet"].file_name.should == "puppet/util/rdoc/generators/puppet_generator.rb"
- end
-
it "should tell RDoc to generate documentation using the Puppet generator" do
@rdoc.expects(:document).with { |args| args.include?("--fmt") and args.include?("puppet") }
@@ -48,18 +34,26 @@ describe Puppet::Util::RDoc do
Puppet::Util::RDoc.rdoc("output", [], "utf-8")
end
- it "should tell RDoc to force updates of indices when RDoc supports it" do
- Options::OptionList.stubs(:options).returns([["--force-update", "-U", 0 ]])
- @rdoc.expects(:document).with { |args| args.include?("--force-update") }
+ describe "with rdoc1", :if => Puppet.features.rdoc1? do
+ it "should install the Puppet HTML Generator into RDoc generators" do
+ Puppet::Util::RDoc.rdoc("output", [])
- Puppet::Util::RDoc.rdoc("output", [])
- end
+ RDoc::RDoc::GENERATORS["puppet"].file_name.should == "puppet/util/rdoc/generators/puppet_generator.rb"
+ end
- it "should not tell RDoc to force updates of indices when RDoc doesn't support it" do
- Options::OptionList.stubs(:options).returns([])
- @rdoc.expects(:document).never.with { |args| args.include?("--force-update") }
+ it "should tell RDoc to force updates of indices when RDoc supports it" do
+ ::Options::OptionList.stubs(:options).returns([["--force-update", "-U", 0 ]])
+ @rdoc.expects(:document).with { |args| args.include?("--force-update") }
- Puppet::Util::RDoc.rdoc("output", [])
+ Puppet::Util::RDoc.rdoc("output", [])
+ end
+
+ it "should not tell RDoc to force updates of indices when RDoc doesn't support it" do
+ ::Options::OptionList.stubs(:options).returns([])
+ @rdoc.expects(:document).never.with { |args| args.include?("--force-update") }
+
+ Puppet::Util::RDoc.rdoc("output", [])
+ end
end
it "should tell RDoc to use the given outputdir" do
diff --git a/spec/unit/util/resource_template_spec.rb b/spec/unit/util/resource_template_spec.rb
index ee4c1b866..0e6037119 100755
--- a/spec/unit/util/resource_template_spec.rb
+++ b/spec/unit/util/resource_template_spec.rb
@@ -6,20 +6,20 @@ require 'puppet/util/resource_template'
describe Puppet::Util::ResourceTemplate do
describe "when initializing" do
it "should fail if the template does not exist" do
- FileTest.expects(:exist?).with("/my/template").returns false
+ Puppet::FileSystem::File.expects(:exist?).with("/my/template").returns false
lambda { Puppet::Util::ResourceTemplate.new("/my/template", mock('resource')) }.should raise_error(ArgumentError)
end
it "should not create the ERB template" do
ERB.expects(:new).never
- FileTest.expects(:exist?).with("/my/template").returns true
+ Puppet::FileSystem::File.expects(:exist?).with("/my/template").returns true
Puppet::Util::ResourceTemplate.new("/my/template", mock('resource'))
end
end
describe "when evaluating" do
before do
- FileTest.stubs(:exist?).returns true
+ Puppet::FileSystem::File.stubs(:exist?).returns true
File.stubs(:read).returns "eh"
@template = stub 'template', :result => nil
diff --git a/spec/unit/util/selinux_spec.rb b/spec/unit/util/selinux_spec.rb
index ac2bc2977..1357b981a 100755
--- a/spec/unit/util/selinux_spec.rb
+++ b/spec/unit/util/selinux_spec.rb
@@ -123,7 +123,8 @@ describe Puppet::Util::SELinux do
it "should return a context if a default context exists" do
self.expects(:selinux_support?).returns true
fstat = stub 'File::Stat', :mode => 0
- File.expects(:lstat).with("/foo").returns fstat
+ stub_file = stub('/foo', :lstat => fstat)
+ Puppet::FileSystem::File.expects(:new).with('/foo').returns stub_file
self.expects(:find_fs).with("/foo").returns "ext3"
Selinux.expects(:matchpathcon).with("/foo", 0).returns [0, "user_u:role_r:type_t:s0"]
get_selinux_default_context("/foo").should == "user_u:role_r:type_t:s0"
@@ -150,7 +151,8 @@ describe Puppet::Util::SELinux do
it "should return nil if matchpathcon returns failure" do
self.expects(:selinux_support?).returns true
fstat = stub 'File::Stat', :mode => 0
- File.expects(:lstat).with("/foo").returns fstat
+ stub_file = stub('/foo', :lstat => fstat)
+ Puppet::FileSystem::File.expects(:new).with('/foo').returns stub_file
self.expects(:find_fs).with("/foo").returns "ext3"
Selinux.expects(:matchpathcon).with("/foo", 0).returns -1
get_selinux_default_context("/foo").should be_nil
diff --git a/spec/unit/util/storage_spec.rb b/spec/unit/util/storage_spec.rb
index ba1675981..582bd2422 100755
--- a/spec/unit/util/storage_spec.rb
+++ b/spec/unit/util/storage_spec.rb
@@ -77,7 +77,7 @@ describe Puppet::Util::Storage do
end
it "should not fail to load" do
- FileTest.exists?(@path).should be_false
+ Puppet::FileSystem::File.exist?(@path).should be_false
Puppet[:statedir] = @path
Puppet::Util::Storage.load
Puppet[:statefile] = @path
@@ -85,7 +85,7 @@ describe Puppet::Util::Storage do
end
it "should not lose its internal state when load() is called" do
- FileTest.exists?(@path).should be_false
+ Puppet::FileSystem::File.exist?(@path).should be_false
Puppet::Util::Storage.cache(:yayness)
Puppet::Util::Storage.state.should == {:yayness=>{}}
@@ -176,12 +176,12 @@ describe Puppet::Util::Storage do
end
it "should create the state file if it does not exist" do
- FileTest.exists?(Puppet[:statefile]).should be_false
+ Puppet::FileSystem::File.exist?(Puppet[:statefile]).should be_false
Puppet::Util::Storage.cache(:yayness)
Puppet::Util::Storage.store
- FileTest.exists?(Puppet[:statefile]).should be_true
+ Puppet::FileSystem::File.exist?(Puppet[:statefile]).should be_true
end
it "should raise an exception if the state file is not a regular file" do
diff --git a/spec/unit/util/suidmanager_spec.rb b/spec/unit/util/suidmanager_spec.rb
index 5962e4362..f8ba883f5 100755
--- a/spec/unit/util/suidmanager_spec.rb
+++ b/spec/unit/util/suidmanager_spec.rb
@@ -231,6 +231,13 @@ describe Puppet::Util::SUIDManager do
output.first.should == 'output'
output.last.should be_a(Process::Status)
end
+
+ it "should log a deprecation notice" do
+ Puppet::Util::Execution.stubs(:execute).returns("success")
+ Puppet.expects(:deprecation_warning).with('Puppet::Util::SUIDManager.run_and_capture is deprecated; please use Puppet::Util::Execution.execute instead.')
+
+ output = Puppet::Util::SUIDManager.run_and_capture 'yay', user[:uid], user[:gid]
+ end
end
end
diff --git a/spec/unit/util/tag_set_spec.rb b/spec/unit/util/tag_set_spec.rb
new file mode 100644
index 000000000..c59674fcb
--- /dev/null
+++ b/spec/unit/util/tag_set_spec.rb
@@ -0,0 +1,46 @@
+#! /usr/bin/env ruby
+require 'spec_helper'
+
+require 'puppet/util/tag_set'
+
+RSpec::Matchers.define :be_one_of do |*expected|
+ match do |actual|
+ expected.include? actual
+ end
+
+ failure_message_for_should do |actual|
+ "expected #{actual.inspect} to be one of #{expected.map(&:inspect).join(' or ')}"
+ end
+end
+
+describe Puppet::Util::TagSet do
+ let(:set) { Puppet::Util::TagSet.new }
+
+ it 'serializes to yaml as an array' do
+ array = ['a', :b, 1, 5.4]
+ set.merge(array)
+
+ Set.new(YAML.load(set.to_yaml)).should == Set.new(array)
+ end
+
+ it 'deserializes from a yaml array' do
+ array = ['a', :b, 1, 5.4]
+
+ Puppet::Util::TagSet.from_yaml(array.to_yaml).should == Puppet::Util::TagSet.new(array)
+ end
+
+ it 'round trips through pson' do
+ array = ['a', 'b', 1, 5.4]
+ set.merge(array)
+
+ tes = Puppet::Util::TagSet.from_pson(PSON.parse(set.to_pson))
+ tes.should == set
+ end
+
+ it 'can join its elements with a string separator' do
+ array = ['a', 'b']
+ set.merge(array)
+
+ set.join(', ').should be_one_of('a, b', 'b, a')
+ end
+end
diff --git a/spec/unit/util/tagging_spec.rb b/spec/unit/util/tagging_spec.rb
index 67b766fe0..248e915e9 100755
--- a/spec/unit/util/tagging_spec.rb
+++ b/spec/unit/util/tagging_spec.rb
@@ -3,92 +3,129 @@ require 'spec_helper'
require 'puppet/util/tagging'
-describe Puppet::Util::Tagging, "when adding tags" do
- before do
- @tagger = Object.new
- @tagger.extend(Puppet::Util::Tagging)
- end
-
- it "should have a method for adding tags" do
- @tagger.should be_respond_to(:tag)
- end
-
- it "should have a method for returning all tags" do
- @tagger.should be_respond_to(:tags)
- end
+describe Puppet::Util::Tagging do
+ let(:tagger) { Object.new.extend(Puppet::Util::Tagging) }
it "should add tags to the returned tag list" do
- @tagger.tag("one")
- @tagger.tags.should be_include("one")
- end
-
- it "should not add duplicate tags to the returned tag list" do
- @tagger.tag("one")
- @tagger.tag("one")
- @tagger.tags.should == ["one"]
+ tagger.tag("one")
+ expect(tagger.tags).to include("one")
end
it "should return a duplicate of the tag list, rather than the original" do
- @tagger.tag("one")
- tags = @tagger.tags
+ tagger.tag("one")
+ tags = tagger.tags
tags << "two"
- @tagger.tags.should_not be_include("two")
+ expect(tagger.tags).to_not include("two")
end
it "should add all provided tags to the tag list" do
- @tagger.tag("one", "two")
- @tagger.tags.should be_include("one")
- @tagger.tags.should be_include("two")
+ tagger.tag("one", "two")
+ expect(tagger.tags).to include("one")
+ expect(tagger.tags).to include("two")
end
it "should fail on tags containing '*' characters" do
- expect { @tagger.tag("bad*tag") }.to raise_error(Puppet::ParseError)
+ expect { tagger.tag("bad*tag") }.to raise_error(Puppet::ParseError)
end
it "should fail on tags starting with '-' characters" do
- expect { @tagger.tag("-badtag") }.to raise_error(Puppet::ParseError)
+ expect { tagger.tag("-badtag") }.to raise_error(Puppet::ParseError)
end
it "should fail on tags containing ' ' characters" do
- expect { @tagger.tag("bad tag") }.to raise_error(Puppet::ParseError)
+ expect { tagger.tag("bad tag") }.to raise_error(Puppet::ParseError)
end
it "should allow alpha tags" do
- expect { @tagger.tag("good_tag") }.to_not raise_error
+ expect { tagger.tag("good_tag") }.not_to raise_error
end
it "should allow tags containing '.' characters" do
- expect { @tagger.tag("good.tag") }.to_not raise_error
+ expect { tagger.tag("good.tag") }.to_not raise_error(Puppet::ParseError)
end
it "should add qualified classes as tags" do
- @tagger.tag("one::two")
- @tagger.tags.should be_include("one::two")
+ tagger.tag("one::two")
+ expect(tagger.tags).to include("one::two")
end
it "should add each part of qualified classes as tags" do
- @tagger.tag("one::two::three")
- @tagger.tags.should be_include("one")
- @tagger.tags.should be_include("two")
- @tagger.tags.should be_include("three")
+ tagger.tag("one::two::three")
+ expect(tagger.tags).to include('one')
+ expect(tagger.tags).to include("two")
+ expect(tagger.tags).to include("three")
end
it "should indicate when the object is tagged with a provided tag" do
- @tagger.tag("one")
- @tagger.should be_tagged("one")
+ tagger.tag("one")
+ expect(tagger).to be_tagged("one")
end
it "should indicate when the object is not tagged with a provided tag" do
- @tagger.should_not be_tagged("one")
+ expect(tagger).to_not be_tagged("one")
end
it "should indicate when the object is tagged with any tag in an array" do
- @tagger.tag("one")
- @tagger.should be_tagged("one","two","three")
+ tagger.tag("one")
+ expect(tagger).to be_tagged("one","two","three")
end
it "should indicate when the object is not tagged with any tag in an array" do
- @tagger.tag("one")
- @tagger.should_not be_tagged("two","three")
+ tagger.tag("one")
+ expect(tagger).to_not be_tagged("two","three")
+ end
+
+ context "when tagging" do
+ it "converts symbols to strings" do
+ tagger.tag(:hello)
+ expect(tagger.tags).to include('hello')
+ end
+
+ it "downcases tags" do
+ tagger.tag(:HEllO)
+ tagger.tag("GooDByE")
+ expect(tagger).to be_tagged("hello")
+ expect(tagger).to be_tagged("goodbye")
+ end
+
+ it "accepts hyphenated tags" do
+ tagger.tag("my-tag")
+ expect(tagger).to be_tagged("my-tag")
+ end
+ end
+
+ context "when querying if tagged" do
+ it "responds true if queried on the entire set" do
+ tagger.tag("one", "two")
+ expect(tagger).to be_tagged("one", "two")
+ end
+
+ it "responds true if queried on a subset" do
+ tagger.tag("one", "two", "three")
+ expect(tagger).to be_tagged("two", "one")
+ end
+
+ it "responds true if queried on an overlapping but not fully contained set" do
+ tagger.tag("one", "two")
+ expect(tagger).to be_tagged("zero", "one")
+ end
+
+ it "responds false if queried on a disjoint set" do
+ tagger.tag("one", "two", "three")
+ expect(tagger).to_not be_tagged("five")
+ end
+
+ it "responds false if queried on the empty set" do
+ expect(tagger).to_not be_tagged
+ end
+ end
+
+ context "when assigning tags" do
+ it "splits a string on ','" do
+ tagger.tags = "one, two, three"
+ expect(tagger).to be_tagged("one")
+ expect(tagger).to be_tagged("two")
+ expect(tagger).to be_tagged("three")
+ end
end
end
diff --git a/spec/unit/util/watcher_spec.rb b/spec/unit/util/watcher_spec.rb
index 64fab9681..33f75ab12 100644
--- a/spec/unit/util/watcher_spec.rb
+++ b/spec/unit/util/watcher_spec.rb
@@ -14,7 +14,10 @@ describe Puppet::Util::Watcher do
let(:filename) { "fake" }
def after_reading_the_sequence(initial, *results)
- expectation = File.stubs(:stat).with(filename)
+ mock_file = mock(filename)
+ Puppet::FileSystem::File.expects(:new).with(filename).at_least(1).returns mock_file
+
+ expectation = mock_file.stubs(:stat)
([initial] + results).each do |result|
expectation = if result.is_a? Class
expectation.raises(result)
diff --git a/spec/unit/util/windows/access_control_entry_spec.rb b/spec/unit/util/windows/access_control_entry_spec.rb
new file mode 100644
index 000000000..b139b0d42
--- /dev/null
+++ b/spec/unit/util/windows/access_control_entry_spec.rb
@@ -0,0 +1,67 @@
+#!/usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/util/windows'
+
+describe "Puppet::Util::Windows::AccessControlEntry", :if => Puppet.features.microsoft_windows? do
+ let(:klass) { Puppet::Util::Windows::AccessControlEntry }
+ let(:sid) { 'S-1-5-18' }
+ let(:mask) { Windows::File::FILE_ALL_ACCESS }
+
+ it "creates an access allowed ace" do
+ ace = klass.new(sid, mask)
+
+ ace.type.should == klass::ACCESS_ALLOWED_ACE_TYPE
+ end
+
+ it "creates an access denied ace" do
+ ace = klass.new(sid, mask, 0, klass::ACCESS_DENIED_ACE_TYPE)
+
+ ace.type.should == klass::ACCESS_DENIED_ACE_TYPE
+ end
+
+ it "creates a non-inherited ace by default" do
+ ace = klass.new(sid, mask)
+
+ ace.should_not be_inherited
+ end
+
+ it "creates an inherited ace" do
+ ace = klass.new(sid, mask, klass::INHERITED_ACE)
+
+ ace.should be_inherited
+ end
+
+ it "creates a non-inherit-only ace by default" do
+ ace = klass.new(sid, mask)
+
+ ace.should_not be_inherit_only
+ end
+
+ it "creates an inherit-only ace" do
+ ace = klass.new(sid, mask, klass::INHERIT_ONLY_ACE)
+
+ ace.should be_inherit_only
+ end
+
+ context "when comparing aces" do
+ let(:ace1) { klass.new(sid, mask, klass::INHERIT_ONLY_ACE, klass::ACCESS_DENIED_ACE_TYPE) }
+ let(:ace2) { klass.new(sid, mask, klass::INHERIT_ONLY_ACE, klass::ACCESS_DENIED_ACE_TYPE) }
+
+ it "returns true if different objects have the same set of values" do
+ ace1.should == ace2
+ end
+
+ it "returns false if different objects have different sets of values" do
+ ace = klass.new(sid, mask)
+ ace.should_not == ace1
+ end
+
+ it "returns true when testing if two objects are eql?" do
+ ace1.eql?(ace2)
+ end
+
+ it "returns false when comparing object identity" do
+ ace1.should_not be_equal(ace2)
+ end
+ end
+end
diff --git a/spec/unit/util/windows/access_control_list_spec.rb b/spec/unit/util/windows/access_control_list_spec.rb
new file mode 100644
index 000000000..66c917b29
--- /dev/null
+++ b/spec/unit/util/windows/access_control_list_spec.rb
@@ -0,0 +1,133 @@
+#!/usr/bin/env ruby
+require 'spec_helper'
+require 'puppet/util/windows'
+
+describe "Puppet::Util::Windows::AccessControlList", :if => Puppet.features.microsoft_windows? do
+ let(:klass) { Puppet::Util::Windows::AccessControlList }
+ let(:system_sid) { 'S-1-5-18' }
+ let(:admins_sid) { 'S-1-5-544' }
+ let(:none_sid) { 'S-1-0-0' }
+
+ let(:system_ace) do
+ Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1)
+ end
+ let(:admins_ace) do
+ Puppet::Util::Windows::AccessControlEntry.new(admins_sid, 0x2)
+ end
+ let(:none_ace) do
+ Puppet::Util::Windows::AccessControlEntry.new(none_sid, 0x3)
+ end
+
+ it "constructs an empty list" do
+ acl = klass.new
+
+ acl.to_a.should be_empty
+ end
+
+ it "supports copy constructor" do
+ aces = klass.new([system_ace]).to_a
+
+ aces.to_a.should == [system_ace]
+ end
+
+ context "appending" do
+ it "appends an allow ace" do
+ acl = klass.new
+ acl.allow(system_sid, 0x1, 0x2)
+
+ acl.first.type.should == klass::ACCESS_ALLOWED_ACE_TYPE
+ end
+
+ it "appends a deny ace" do
+ acl = klass.new
+ acl.deny(system_sid, 0x1, 0x2)
+
+ acl.first.type.should == klass::ACCESS_DENIED_ACE_TYPE
+ end
+
+ it "always appends, never overwrites an ACE" do
+ acl = klass.new([system_ace])
+ acl.allow(admins_sid, admins_ace.mask, admins_ace.flags)
+
+ aces = acl.to_a
+ aces.size.should == 2
+ aces[0].should == system_ace
+ aces[1].sid.should == admins_sid
+ aces[1].mask.should == admins_ace.mask
+ aces[1].flags.should == admins_ace.flags
+ end
+ end
+
+ context "reassigning" do
+ it "preserves the mask from the old sid when reassigning to the new sid" do
+ dacl = klass.new([system_ace])
+
+ dacl.reassign!(system_ace.sid, admins_ace.sid)
+ # we removed system, so ignore prepended ace
+ ace = dacl.to_a[1]
+ ace.sid.should == admins_sid
+ ace.mask.should == system_ace.mask
+ end
+
+ it "matches multiple sids" do
+ dacl = klass.new([system_ace, system_ace])
+
+ dacl.reassign!(system_ace.sid, admins_ace.sid)
+ # we removed system, so ignore prepended ace
+ aces = dacl.to_a
+ aces.size.should == 3
+ aces.to_a[1,2].each do |ace|
+ ace.sid.should == admins_ace.sid
+ end
+ end
+
+ it "preserves aces for sids that don't match, in their original order" do
+ dacl = klass.new([system_ace, admins_ace])
+
+ dacl.reassign!(system_sid, none_sid)
+ aces = dacl.to_a
+ aces[1].sid == admins_ace.sid
+ end
+
+ it "preserves inherited aces, even if the sids match" do
+ flags = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE
+ inherited_ace = Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1, flags)
+ dacl = klass.new([inherited_ace, system_ace])
+ dacl.reassign!(system_sid, none_sid)
+ aces = dacl.to_a
+
+ aces[0].sid.should == system_sid
+ end
+
+ it "prepends an explicit ace for the new sid with the same mask and basic inheritance as the inherited ace" do
+ expected_flags =
+ Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE |
+ Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE |
+ Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE
+
+ flags = Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE | expected_flags
+
+ inherited_ace = Puppet::Util::Windows::AccessControlEntry.new(system_sid, 0x1, flags)
+ dacl = klass.new([inherited_ace])
+ dacl.reassign!(system_sid, none_sid)
+ aces = dacl.to_a
+
+ aces.size.should == 2
+ aces[0].sid.should == none_sid
+ aces[0].should_not be_inherited
+ aces[0].flags.should == expected_flags
+
+ aces[1].sid.should == system_sid
+ aces[1].should be_inherited
+ end
+
+ it "makes a copy of the ace prior to modifying it" do
+ arr = [system_ace]
+
+ acl = klass.new(arr)
+ acl.reassign!(system_sid, none_sid)
+
+ arr[0].sid.should == system_sid
+ end
+ end
+end
diff --git a/spec/unit/util/windows/root_certs_spec.rb b/spec/unit/util/windows/root_certs_spec.rb
index 155707e4e..6b1d9891e 100755
--- a/spec/unit/util/windows/root_certs_spec.rb
+++ b/spec/unit/util/windows/root_certs_spec.rb
@@ -3,13 +3,15 @@ require 'spec_helper'
require 'puppet/util/windows'
describe "Puppet::Util::Windows::RootCerts", :if => Puppet::Util::Platform.windows? do
- let(:klass) { Puppet::Util::Windows::RootCerts }
- let(:x509) { 'mycert' }
-
- context '#each' do
- it "should enumerate each root cert" do
- klass.expects(:load_certs).returns([x509])
- klass.instance.to_a.should == [x509]
- end
+ let(:x509_store) { Puppet::Util::Windows::RootCerts.instance.to_a }
+
+ it "should return at least one X509 certificate" do
+ expect(x509_store.to_a).to have_at_least(1).items
+ end
+
+ it "should return an X509 certificate with a subject" do
+ x509 = x509_store.first
+
+ expect(x509.subject.to_s).to match(/CN=.*/)
end
end
diff --git a/spec/unit/util/windows/security_descriptor_spec.rb b/spec/unit/util/windows/security_descriptor_spec.rb
new file mode 100644
index 000000000..61db2f598
--- /dev/null
+++ b/spec/unit/util/windows/security_descriptor_spec.rb
@@ -0,0 +1,117 @@
+#!/usr/bin/env ruby
+
+require 'spec_helper'
+require 'puppet/util/windows'
+
+describe "Puppet::Util::Windows::SecurityDescriptor", :if => Puppet.features.microsoft_windows? do
+ let(:system_sid) { Win32::Security::SID::LocalSystem }
+ let(:admins_sid) { Win32::Security::SID::BuiltinAdministrators }
+ let(:group_sid) { Win32::Security::SID::Nobody }
+ let(:new_sid) { 'S-1-5-32-500-1-2-3' }
+
+ def empty_dacl
+ Puppet::Util::Windows::AccessControlList.new
+ end
+
+ def system_ace_dacl
+ dacl = Puppet::Util::Windows::AccessControlList.new
+ dacl.allow(system_sid, 0x1)
+ dacl
+ end
+
+ context "owner" do
+ it "changes the owner" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, system_ace_dacl)
+ sd.owner = new_sid
+
+ sd.owner.should == new_sid
+ end
+
+ it "performs a noop if the new owner is the same as the old one" do
+ dacl = system_ace_dacl
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, dacl)
+ sd.owner = sd.owner
+
+ sd.dacl.object_id.should == dacl.object_id
+ end
+
+ it "prepends SYSTEM when security descriptor owner is no longer SYSTEM" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, system_ace_dacl)
+ sd.owner = new_sid
+
+ aces = sd.dacl.to_a
+ aces.size.should == 2
+ aces[0].sid.should == system_sid
+ aces[1].sid.should == new_sid
+ end
+
+ it "does not prepend SYSTEM when DACL already contains inherited SYSTEM ace" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(admins_sid, system_sid, empty_dacl)
+ sd.dacl.allow(admins_sid, 0x1)
+ sd.dacl.allow(system_sid, 0x1, Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE)
+ sd.owner = new_sid
+
+ aces = sd.dacl.to_a
+ aces.size.should == 2
+ aces[0].sid.should == new_sid
+ end
+
+ it "does not prepend SYSTEM when security descriptor owner wasn't SYSTEM" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(group_sid, group_sid, empty_dacl)
+ sd.dacl.allow(group_sid, 0x1)
+ sd.owner = new_sid
+
+ aces = sd.dacl.to_a
+ aces.size.should == 1
+ aces[0].sid.should == new_sid
+ end
+ end
+
+ context "group" do
+ it "changes the group" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, system_ace_dacl)
+ sd.group = new_sid
+
+ sd.group.should == new_sid
+ end
+
+ it "performs a noop if the new group is the same as the old one" do
+ dacl = system_ace_dacl
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(system_sid, group_sid, dacl)
+ sd.group = sd.group
+
+ sd.dacl.object_id.should == dacl.object_id
+ end
+
+ it "prepends SYSTEM when security descriptor group is no longer SYSTEM" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(new_sid, system_sid, system_ace_dacl)
+ sd.group = new_sid
+
+ aces = sd.dacl.to_a
+ aces.size.should == 2
+ aces[0].sid.should == system_sid
+ aces[1].sid.should == new_sid
+ end
+
+ it "does not prepend SYSTEM when DACL already contains inherited SYSTEM ace" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(admins_sid, admins_sid, empty_dacl)
+ sd.dacl.allow(admins_sid, 0x1)
+ sd.dacl.allow(system_sid, 0x1, Puppet::Util::Windows::AccessControlEntry::INHERITED_ACE)
+ sd.group = new_sid
+
+ aces = sd.dacl.to_a
+ aces.size.should == 2
+ aces[0].sid.should == new_sid
+ end
+
+ it "does not prepend SYSTEM when security descriptor group wasn't SYSTEM" do
+ sd = Puppet::Util::Windows::SecurityDescriptor.new(group_sid, group_sid, empty_dacl)
+ sd.dacl.allow(group_sid, 0x1)
+ sd.group = new_sid
+
+ aces = sd.dacl.to_a
+ aces.size.should == 1
+ aces[0].sid.should == new_sid
+ end
+ end
+end
diff --git a/spec/unit/util/windows/sid_spec.rb b/spec/unit/util/windows/sid_spec.rb
index d53e93ae6..770512188 100755
--- a/spec/unit/util/windows/sid_spec.rb
+++ b/spec/unit/util/windows/sid_spec.rb
@@ -15,6 +15,45 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows?
let(:unknown_sid) { 'S-0-0-0' }
let(:unknown_name) { 'chewbacca' }
+ context "#octet_string_to_sid_object" do
+ it "should properly convert an array of bytes for the local Administrator SID" do
+ host = '.'
+ username = 'Administrator'
+ admin = WIN32OLE.connect("WinNT://#{host}/#{username},user")
+ converted = subject.octet_string_to_sid_object(admin.objectSID)
+
+ converted.should == Win32::Security::SID.new(username, host)
+ converted.should be_an_instance_of Win32::Security::SID
+ end
+
+ it "should properly convert an array of bytes for a well-known SID" do
+ bytes = [1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0]
+ converted = subject.octet_string_to_sid_object(bytes)
+
+ converted.should == Win32::Security::SID.new('SYSTEM')
+ converted.should be_an_instance_of Win32::Security::SID
+ end
+
+ it "should raise an error for non-array input" do
+ expect {
+ subject.octet_string_to_sid_object(invalid_sid)
+ }.to raise_error(Puppet::Error, /Octet string must be an array of bytes/)
+ end
+
+ it "should raise an error for an empty byte array" do
+ expect {
+ subject.octet_string_to_sid_object([])
+ }.to raise_error(Puppet::Error, /Octet string must be an array of bytes/)
+ end
+
+ it "should raise an error for a malformed byte array" do
+ expect {
+ invalid_octet = [1]
+ subject.octet_string_to_sid_object(invalid_octet)
+ }.to raise_error(Win32::Security::SID::Error, /No mapping between account names and security IDs was done./)
+ end
+ end
+
context "#name_to_sid" do
it "should return nil if the account does not exist" do
subject.name_to_sid(unknown_name).should be_nil
@@ -28,6 +67,10 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows?
subject.name_to_sid('SYSTEM').should == subject.name_to_sid('system')
end
+ it "should be leading and trailing whitespace-insensitive" do
+ subject.name_to_sid('SYSTEM').should == subject.name_to_sid(' SYSTEM ')
+ end
+
it "should accept domain qualified account names" do
subject.name_to_sid('NT AUTHORITY\SYSTEM').should == sid
end
@@ -37,6 +80,32 @@ describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows?
end
end
+ context "#name_to_sid_object" do
+ it "should return nil if the account does not exist" do
+ subject.name_to_sid_object(unknown_name).should be_nil
+ end
+
+ it "should return a Win32::Security::SID instance for any valid sid" do
+ subject.name_to_sid_object(sid).should be_an_instance_of(Win32::Security::SID)
+ end
+
+ it "should accept unqualified account name" do
+ subject.name_to_sid_object('SYSTEM').to_s.should == sid
+ end
+
+ it "should be case-insensitive" do
+ subject.name_to_sid_object('SYSTEM').should == subject.name_to_sid_object('system')
+ end
+
+ it "should be leading and trailing whitespace-insensitive" do
+ subject.name_to_sid_object('SYSTEM').should == subject.name_to_sid_object(' SYSTEM ')
+ end
+
+ it "should accept domain qualified account names" do
+ subject.name_to_sid_object('NT AUTHORITY\SYSTEM').to_s.should == sid
+ end
+ end
+
context "#sid_to_name" do
it "should return nil if given a sid for an account that doesn't exist" do
subject.sid_to_name(unknown_sid).should be_nil
diff --git a/spec/unit/util/yaml_spec.rb b/spec/unit/util/yaml_spec.rb
index 960388ade..978afb0a2 100644
--- a/spec/unit/util/yaml_spec.rb
+++ b/spec/unit/util/yaml_spec.rb
@@ -33,6 +33,20 @@ describe Puppet::Util::Yaml do
expect { Puppet::Util::Yaml.load_file("not\0allowed") }.to raise_error(Puppet::Util::Yaml::YamlLoadError, /null byte/)
end
+ context "when the file is empty" do
+ it "returns false" do
+ Puppet::FileSystem::File.new(filename).touch
+
+ expect(Puppet::Util::Yaml.load_file(filename)).to be_false
+ end
+
+ it "allows return value to be overridden" do
+ Puppet::FileSystem::File.new(filename).touch
+
+ expect(Puppet::Util::Yaml.load_file(filename, {})).to eq({})
+ end
+ end
+
def write_file(name, contents)
File.open(name, "w") do |fh|
fh.write(contents)
diff --git a/spec/unit/util_spec.rb b/spec/unit/util_spec.rb
index 48b6e6fc5..58cc4bbd8 100755
--- a/spec/unit/util_spec.rb
+++ b/spec/unit/util_spec.rb
@@ -19,7 +19,7 @@ describe Puppet::Util do
end
def get_mode(file)
- File.lstat(file).mode & 07777
+ Puppet::FileSystem::File.new(file).lstat.mode & 07777
end
end
@@ -449,7 +449,7 @@ describe Puppet::Util do
it "should copy the permissions of the source file before yielding on Unix", :if => !Puppet.features.microsoft_windows? do
set_mode(0555, target.path)
- inode = File.stat(target.path).ino
+ inode = Puppet::FileSystem::File.new(target.path).stat.ino
yielded = false
subject.replace_file(target.path, 0600) do |fh|
@@ -458,19 +458,19 @@ describe Puppet::Util do
end
yielded.should be_true
- File.stat(target.path).ino.should_not == inode
+ Puppet::FileSystem::File.new(target.path).stat.ino.should_not == inode
get_mode(target.path).should == 0555
end
it "should use the default permissions if the source file doesn't exist" do
new_target = target.path + '.foo'
- File.should_not be_exist(new_target)
+ Puppet::FileSystem::File.exist?(new_target).should be_false
begin
subject.replace_file(new_target, 0555) {|fh| fh.puts "foo" }
get_mode(new_target).should == 0555
ensure
- File.unlink(new_target) if File.exists?(new_target)
+ Puppet::FileSystem::File.unlink(new_target) if Puppet::FileSystem::File.exist?(new_target)
end
end
@@ -502,14 +502,14 @@ describe Puppet::Util do
{:string => '664', :number => 0664, :symbolic => "ug=rw-,o=r--" }.each do |label,mode|
it "should support #{label} format permissions" do
new_target = target.path + "#{mode}.foo"
- File.should_not be_exist(new_target)
+ Puppet::FileSystem::File.exist?(new_target).should be_false
begin
subject.replace_file(new_target, mode) {|fh| fh.puts "this is an interesting content" }
get_mode(new_target).should == 0664
ensure
- File.unlink(new_target) if File.exists?(new_target)
+ Puppet::FileSystem::File.unlink(new_target) if Puppet::FileSystem::File.exist?(new_target)
end
end
end
diff --git a/tasks/ci.rake b/tasks/ci.rake
new file mode 100644
index 000000000..ea4b95383
--- /dev/null
+++ b/tasks/ci.rake
@@ -0,0 +1,29 @@
+require 'yaml'
+require 'time'
+
+namespace "ci" do
+ task :spec do
+ ENV["LOG_SPEC_ORDER"] = "true"
+ sh %{rspec -r yarjuf -f JUnit -o result.xml -fd spec}
+ end
+
+ desc "Tar up the acceptance/ directory so that package test runs have tests to run against."
+ task :acceptance_artifacts => :tag_creator do
+ Dir.chdir("acceptance") do
+ rm_f "acceptance-artifacts.tar.gz"
+ sh "tar -czv --exclude .bundle -f acceptance-artifacts.tar.gz *"
+ end
+ end
+
+ task :tag_creator do
+ Dir.chdir("acceptance") do
+ File.open('creator.txt', 'w') do |fh|
+ YAML.dump({
+ 'creator_id' => ENV['CREATOR'] || ENV['BUILD_URL'] || 'unknown',
+ 'created_on' => Time.now.iso8601,
+ 'commit' => (`git log -1 --oneline` rescue "unknown: #{$!}")
+ }, fh)
+ end
+ end
+ end
+end