summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-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
339 files changed, 7105 insertions, 4455 deletions
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