pax_global_header00006660000000000000000000000064147013174630014520gustar00rootroot0000000000000052 comment=10486dd9d1ace5558d99ddf95b8a5e93f7a2058e puppetlabs-puppet-789f600/000077500000000000000000000000001470131746300154775ustar00rootroot00000000000000puppetlabs-puppet-789f600/.gitattributes000066400000000000000000000010141470131746300203660ustar00rootroot00000000000000# Disable NL -> CRNL translation on Windows. This is necessary because the files on disk must # match the checksums in metadata.json. spec/fixtures/integration/application/module/environments/direnv/modules/nginx/README -text spec/fixtures/integration/application/module/environments/direnv/modules/nginx/metadata.json -text spec/fixtures/integration/application/module/environments/direnv/modules/nginx/Modulefile -text spec/fixtures/integration/application/module/environments/direnv/modules/nginx/manifests/init.pp -text puppetlabs-puppet-789f600/.github/000077500000000000000000000000001470131746300170375ustar00rootroot00000000000000puppetlabs-puppet-789f600/.github/release.yml000066400000000000000000000002251470131746300212010ustar00rootroot00000000000000changelog: categories: - title: Features & Enhancements labels: - enhancement - title: Bug Fixes labels: - bug puppetlabs-puppet-789f600/.github/workflows/000077500000000000000000000000001470131746300210745ustar00rootroot00000000000000puppetlabs-puppet-789f600/.github/workflows/backport.yml000066400000000000000000000013201470131746300234200ustar00rootroot00000000000000name: Backport merged pull request on: pull_request_target: types: [labeled] permissions: contents: write # so it can comment pull-requests: write # so it can create pull requests jobs: backport: name: Backport merged pull request runs-on: ubuntu-latest # For security reasons, we don't want to checkout and run arbitrary code when # using the pull_request_target trigger. So restrict this to cases where the # backport label is applied to an already merged PR. if: github.event.pull_request.merged && contains(github.event.label.name, 'backport') steps: - uses: actions/checkout@v4 - name: Create backport pull requests uses: korthout/backport-action@v1 puppetlabs-puppet-789f600/.github/workflows/checks.yaml000066400000000000000000000017031470131746300232210ustar00rootroot00000000000000--- name: Checks on: push: branches: [main] pull_request: branches: [main] permissions: contents: read jobs: checks: name: ${{ matrix.cfg.check }} strategy: matrix: cfg: - {check: rubocop, os: ubuntu-latest, ruby: '3.1'} - {check: warnings, os: ubuntu-latest, ruby: '3.1'} runs-on: ${{ matrix.cfg.os }} steps: - name: Checkout current PR uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install ruby version ${{ matrix.cfg.ruby }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} - name: Update rubygems and install gems run: | gem update --system --silent --no-document bundle config set without packaging documentation bundle install --jobs 4 --retry 3 - name: Run ${{ matrix.cfg.check }} check run: bundle exec rake ${{ matrix.cfg.check }} puppetlabs-puppet-789f600/.github/workflows/jira.yml000066400000000000000000000006111470131746300225420ustar00rootroot00000000000000--- name: Export issue to Jira on: issues: types: [labeled] permissions: issues: write jobs: export: uses: "puppetlabs/phoenix-github-actions/.github/workflows/jira.yml@main" with: jira-project: PUP jira-base-url: ${{ vars.jira_base_url }} jira-user-email: ${{ vars.jira_user_email }} secrets: jira-api-token: ${{ secrets.JIRA_ISSUES_ACTION }} puppetlabs-puppet-789f600/.github/workflows/mend.yaml000066400000000000000000000017631470131746300227120ustar00rootroot00000000000000--- name: Mend Monitor on: push: branches: - main jobs: mend_monitor: if: ${{ github.repository_owner == 'puppetlabs' }} runs-on: ubuntu-latest name: Mend Monitor steps: - name: Checkout current PR uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: ruby-version: 3.1 - name: Create lock run: bundle lock - uses: actions/setup-java@v3 with: distribution: 'temurin' java-version: '17' - name: Download Mend run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar - name: Run Mend run: java -jar wss-unified-agent.jar env: WS_APIKEY: ${{ secrets.MEND_API_KEY }} WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent WS_USERKEY: ${{ secrets.MEND_TOKEN }} WS_PRODUCTNAME: Puppet Agent WS_PROJECTNAME: ${{ github.event.repository.name }} puppetlabs-puppet-789f600/.github/workflows/references.yaml000066400000000000000000000027041470131746300241040ustar00rootroot00000000000000--- name: Generate References on: push: branches: - main permissions: contents: write jobs: generate_references: if: ${{ github.repository_owner == 'puppetlabs' }} runs-on: ubuntu-latest name: Generate References env: BUNDLE_WITH: "documentation" BUNDLE_WITHOUT: "features packaging" steps: - name: Checkout current PR uses: actions/checkout@v4 with: token: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} - name: Setup Ruby uses: ruby/setup-ruby@v1 with: ruby-version: 3.2 bundler-cache: true - name: Setup Pandoc uses: pandoc/actions/setup@d940685d5968400c91029147adbd612deb7696b0 with: version: 3.1.8 - name: Generate References id: generate-references run: | bundle exec rake references:all git --no-pager diff --exit-code --ignore-matching-lines='This page was generated from the Puppet source' --ignore-matching-lines='built_from_commit:' man references || echo 'commit=true' >> "$GITHUB_OUTPUT" - name: Commit and Push if: ${{ steps.generate-references.outputs.commit == 'true' }} uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 with: author_name: GitHub Actions author_email: actions@github.com message: 'Update references [no-promote]' add: 'man references' push: true puppetlabs-puppet-789f600/.github/workflows/rspec_tests.yaml000066400000000000000000000053241470131746300243220ustar00rootroot00000000000000--- name: RSpec tests on: push: branches: [main] pull_request: branches: [main] permissions: contents: read jobs: rspec_tests: name: ${{ matrix.cfg.os }}(ruby ${{ matrix.cfg.ruby }}) strategy: matrix: cfg: - {os: ubuntu-latest, ruby: '3.1'} - {os: ubuntu-20.04, ruby: '3.2'} # openssl 1.1.1 - {os: ubuntu-22.04, ruby: '3.2'} # openssl 3 - {os: ubuntu-22.04, ruby: '3.3'} # openssl 3 / latest Ruby - {os: ubuntu-latest, ruby: 'jruby-9.4.3.0'} - {os: windows-2019, ruby: '3.1'} - {os: windows-2019, ruby: '3.2'} # openssl 3 - {os: windows-2019, ruby: '3.3'} # openssl 3 / latest Ruby runs-on: ${{ matrix.cfg.os }} env: BUNDLE_SET: "without packaging documentation" steps: - name: Checkout current PR uses: actions/checkout@v4 - name: Install ruby version ${{ matrix.cfg.ruby }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.cfg.ruby }} bundler-cache: true - name: Run tests on Windows if: runner.os == 'Windows' run: | # https://github.com/ruby/ruby/pull/2791/files#diff-ff5ff976e81bebd977f0834e60416abbR97-R100 # Actions uses UTF8, causes test failures, similar to normal OS setup $PSDefaultParameterValues['*:Encoding'] = 'utf8' [Console]::OutputEncoding = [System.Text.Encoding]::GetEncoding("IBM437") [Console]::InputEncoding = [System.Text.Encoding]::GetEncoding("IBM437") $Env:LOG_SPEC_ORDER = 'true' # debug information chcp Get-WinSystemLocale Get-ChildItem Env: | % { Write-Output "$($_.Key): $($_.Value)" } # list current OpenSSL install gem list openssl ruby -ropenssl -e 'puts "OpenSSL Version - #{OpenSSL::OPENSSL_VERSION}"; puts "OpenSSL Library Version - #{OpenSSL::OPENSSL_LIBRARY_VERSION}"' Get-Content Gemfile.lock ruby -v gem --version bundle --version # Run tests bundle exec rake parallel:spec[2] - name: Run tests on Linux if: runner.os == 'Linux' run: | # debug information gem list openssl ruby -ropenssl -e 'puts "OpenSSL Version - #{OpenSSL::OPENSSL_VERSION}"; puts "OpenSSL Library Version - #{OpenSSL::OPENSSL_LIBRARY_VERSION}"' cat Gemfile.lock ruby -v gem --version bundle --version if [[ ${{ matrix.cfg.ruby }} =~ "jruby" ]]; then export _JAVA_OPTIONS='-Xmx1024m -Xms512m' # workaround for PUP-10683 sudo apt remove rpm fi bundle exec rake parallel:spec[2] puppetlabs-puppet-789f600/.gitignore000066400000000000000000000011201470131746300174610ustar00rootroot00000000000000.rspec results tags .*.sw[op] test.pp # YARD generated documentation .yardoc /doc # Now that there is a gemfile, RVM ignores rvmrc in parent directories, so a local one is required # to work around that. Which we don't want committed, so we can ignore it here. /.rvmrc .bundle/ .byebug_history /ext/packaging/ /pkg/ Gemfile.lock Gemfile.local Guardfile puppet-acceptance/ /.project .ruby-version .ruby-gemset /acceptance/junit /acceptance/log /acceptance/.beaker # emacs backup files *~ /*.samples coverage/ # Files and directory added by RubyMine IDE *.iml .rakeTasks .idea/ spec_order.txt puppetlabs-puppet-789f600/.gitmodules000066400000000000000000000003321470131746300176520ustar00rootroot00000000000000[submodule "benchmarks/full_catalog/puppetlabs-puppetserver_perf_control"] path = benchmarks/full_catalog/puppetlabs-puppetserver_perf_control url = https://github.com/puppetlabs/puppetlabs-puppetserver_perf_control puppetlabs-puppet-789f600/.mailmap000066400000000000000000000164771470131746300171370ustar00rootroot00000000000000Adrien Thebo Adrien Thebo Adrien Thebo adrienthebo Allen Ballman ballman Andrew Shafer Andrew Shafer Andrew Shafer Andrew Shafer Andrew Shafer shafer Anselm Strauss Anselm Strauss Anselm Strauss Anselm Strauss Ben Hengst ben hengst Ben Hughes Ben H Ben Hughes Ben Hughes Blake Barnett shadoi Brice Figureau Brice Figureau Bryan Kearney Bryan Kearney Cameron Thomas Cameron Thomas Carl Caum Carl Caum Chris Price Chris Price Chris Price cprice Christian Hofstaedtler Christian Hofstaedtler Dan Bode Dan Bode Dan Bode Dan Bode Dan Bode Dan Bode Daniel Pittman Daniel Pittman David Lutterkort David Lutterkort David Lutterkort lutter David Lutterkort lutter David Schmitt David Schmitt Deepak Giridharagopal Deepak Giridharagopal Dominic Maraglia Donavan Miller donavan Eric Sorenson Eric Sorenson Eric Sorenson Eric Sorenson Eric Sorenson Eric Sorenson Francois Deppierraz Francois Deppierraz Garrett Honeycutt Garrett Honeycutt Gary Larizza Gary Larizza Greg Sutcliffe Greg Sutcliffe James Turnbull James Turnbull James Turnbull James Turnbull James Turnbull James Turnbull James Turnbull root Jeff McCune Jeff McCune Jeffrey J McCune Jeff McCune Jeffrey McCune Jeff McCune mccune Jeff McCune mccune Jeff Weiss Jeff Weiss Jesse Wolfe Jesse Wolfe Jos Boumans josb Josh Cooper Josh Cooper Josh Cooper joshcooper Joshua Harlan Lifton lifton Justin Stoller Justin Stoller Kelsey Hightower Kelsey Hightower Kelsey Hightower Kelsey Hightower Ken Barber Ken Barber Luke Kanies Luke Kanies Luke Kaines Luke Kanies Luke Kanies Luke Kanies Luke Kanies Luke Kanies luke Markus Roberts Markus Roberts Markus Roberts Markus Roberts Markus Roberts markus Martin Englund Martin Englund Matt Palmer mpalmer Matt Robinson Matt Robinson Matthaus Owens Matthaus Litteken Matthaus Owens Matthaus Litteken Matthaus Owens Matthaus Owens Michael Stahnke stahnma Moses Mendoza MosesMendoza Nan Liu Nan Liu Nick Fagerlund nfagerlund Nick Fagerlund nfagerlund Nigel Kersten Nigel Kersten Nigel Kersten Nigel Kersten Ohad Levy Ohad Levy Patrick Carlisle Patrick Patrick Carlisle pcarlisle Paul Lathrop Paul Lathrop Paul Lathrop Paul Lathrop Peter Meier duritong Peter Meier mh Peter Mørch peter Pieter van de Bruggen Pieter van de Bruggen Rahul Gopinath rahul Rahul Gopinath rahul Rahul Gopinath rahul Rahul Gopinath vrthra <9@vrtra.net> Rein Henrichs Rein Henrichs Rudy Gevaert rgevaert Rudy Gevaert rgevaert Russ Allbery Russ Allbery Sean E. Millichamp Sean Millichamp Sean E. Millichamp Sean Millichamp Steve McIintosh steve mcintosh Thom May Thom May Thom May Thom May puppetlabs-puppet-789f600/.noexec.yaml000066400000000000000000000000461470131746300177220ustar00rootroot00000000000000--- exclude: - gem - rake - rspec puppetlabs-puppet-789f600/.rubocop.yml000066400000000000000000000177051470131746300177630ustar00rootroot00000000000000inherit_from: .rubocop_todo.yml require: - rubocop-i18n - rubocop-performance - rubocop-rake - rubocop-rspec AllCops: TargetRubyVersion: 3.1 Include: - 'lib/**/*.rb' - 'ext/**/*.rb' Exclude: - '**/*.erb' - 'acceptance/**/*' - 'spec/**/*' - 'tasks/**/*' - 'ext/suse/puppet.spec' - 'lib/puppet/vendor/**/*' - 'lib/puppet/pops/model/ast.rb' - 'lib/puppet/pops/parser/eparser.rb' # The formatting of defaults is unusual, so let's skip layout cops. Layout: Exclude: - 'lib/puppet/defaults.rb' # We don't mind when module and class keywords are aligned. Layout/IndentationWidth: AllowedPatterns: ['^\s*module'] Layout/LineEndStringConcatenationIndentation: Enabled: true # Enabling this cop will remove raising RuntimeErrors and cause spec test # failures Style/RedundantException: Exclude: - 'lib/puppet/file_bucket/dipper.rb' - 'lib/puppet/forge.rb' - 'lib/puppet/module_tool/applications/unpacker.rb' - 'lib/puppet/module_tool/local_tarball.rb' - 'lib/puppet/module_tool/tar.rb' - 'lib/puppet/pops/types/class_loader.rb' # Enabling this cop causes a plethora of failed rspec tests, mostly # Errno::ENAMETOOLONG and Puppet::Context::UndefinedBindingError: Unable to # lookup 'environments' errors Style/RedundantSelfAssignment: Exclude: - 'lib/puppet/context.rb' # Enabling this cop causes failures in rb_tree_map_spec relating to important # function slike being unable to delete nodes and returning nil when the key # cannot be found Style/PreferredHashMethods: Enabled: false # Explicitly enables this cop new in 1.7 Layout/SpaceBeforeBrackets: Enabled: true # puppet uses symbol booleans in types and providers to work around long standing # bugs when trying to manage falsey pararameters and properties Lint/BooleanSymbol: Enabled: true Exclude: - 'lib/puppet/type.rb' - 'lib/puppet/type/**/*.rb' - 'lib/puppet/provider/**/*.rb' - 'lib/puppet/reference/providers.rb' - 'lib/puppet/parameter/value.rb' Metrics/AbcSize: Enabled: false Metrics/BlockLength: Enabled: false Metrics/BlockNesting: Enabled: false Metrics/ClassLength: Enabled: false Metrics/CyclomaticComplexity: Enabled: false Metrics/MethodLength: Enabled: false Metrics/ModuleLength: Enabled: false Metrics/ParameterLists: Enabled: false Metrics/PerceivedComplexity: Enabled: false Naming/AccessorMethodName: Enabled: false Naming/BinaryOperatorParameterName: Enabled: false Naming/BlockParameterName: Exclude: - 'lib/puppet/util/windows/daemon.rb' - 'lib/puppet/util/windows/user.rb' Naming/ClassAndModuleCamelCase: Exclude: - 'lib/puppet/ffi/windows/structs.rb' - 'lib/puppet/pops/validation/checker4_0.rb' - 'lib/puppet/pops/validation/validator_factory_4_0.rb' - 'lib/puppet/util/windows/root_certs.rb' - 'lib/puppet/util/windows/security.rb' - 'lib/puppet/util/windows/user.rb' Naming/ConstantName: Exclude: - 'lib/puppet/graph/relationship_graph.rb' - 'lib/puppet/indirector/hiera.rb' - 'lib/puppet/provider/package/sun.rb' - 'lib/puppet/resource/type.rb' - 'lib/puppet/type/schedule.rb' - 'lib/puppet/type/tidy.rb' - 'lib/puppet/util.rb' - 'lib/puppet/util/colors.rb' - 'lib/puppet/util/execution.rb' - 'lib/puppet/util/symbolic_file_mode.rb' - 'lib/puppet/util/tagging.rb' - 'lib/puppet/util/windows/adsi.rb' - 'lib/puppet/util/windows/sid.rb' - 'lib/puppet/util/yaml.rb' Naming/HeredocDelimiterNaming: Enabled: false # Exclude existing violations to avoid breaking changes Naming/MemoizedInstanceVariableName: Exclude: - 'lib/puppet/module_tool/applications/installer.rb' - 'lib/puppet/pops/types/type_factory.rb' - 'lib/puppet/provider/package/portage.rb' - 'lib/puppet/resource.rb' Naming/MethodName: Exclude: - 'lib/puppet/functions/**/*' - 'lib/puppet/parser/ast/pops_bridge.rb' - 'lib/puppet/pops/**/*' - 'lib/puppet/util/windows/**/*' Naming/MethodParameterName: Enabled: false Naming/PredicateName: ForbiddenPrefixes: [] Naming/RescuedExceptionsVariableName: Enabled: false Naming/VariableName: Exclude: - 'ext/windows/service/daemon.rb' - 'lib/puppet/agent.rb' - 'lib/puppet/application/describe.rb' - 'lib/puppet/pops/lookup/hiera_config.rb' - 'lib/puppet/pops/validation/checker4_0.rb' - 'lib/puppet/provider/package/pip.rb' - 'lib/puppet/provider/package/windows/exe_package.rb' - 'lib/puppet/provider/package/windows/msi_package.rb' - 'lib/puppet/ssl/ssl_provider.rb' - 'lib/puppet/util/windows/adsi.rb' - 'lib/puppet/util/windows/daemon.rb' - 'lib/puppet/util/windows/error.rb' - 'lib/puppet/util/windows/user.rb' Naming/VariableNumber: Enabled: false Performance/AncestorsInclude: # new in 1.7 Enabled: true Performance/BigDecimalWithNumericArgument: # new in 1.7 Enabled: true Performance/ConcurrentMonotonicTime: # new in 1.12 Enabled: true Performance/MapCompact: # new in 1.11 Enabled: true Performance/RedundantSortBlock: # new in 1.7 Enabled: true Performance/ReverseFirst: # new in 1.7 Enabled: true RSpec/BeEq: # new in 2.9.0 Enabled: true RSpec/BeNil: # new in 2.9.0 Enabled: true RSpec/ExcessiveDocstringSpacing: # new in 2.5 Enabled: true RSpec/IdenticalEqualityAssertion: # new in 2.4 Enabled: true RSpec/SubjectDeclaration: # new in 2.5 Enabled: true RSpec/VerifiedDoubleReference: # new in 2.10.0 Enabled: true RSpec/FactoryBot/SyntaxMethods: # new in 2.7 Enabled: true RSpec/Rails/AvoidSetupHook: # new in 2.4 Enabled: true Style/AutoResourceCleanup: Enabled: true Style/CaseEquality: Enabled: true Style/CaseLikeIf: Enabled: true Style/ClassCheck: Enabled: true Style/ClassEqualityComparison: Enabled: true Style/ClassMethods: Enabled: true Style/CollectionCompact: Enabled: true Style/ColonMethodCall: Enabled: true Style/CombinableLoops: Enabled: true Exclude: - 'lib/puppet/graph/relationship_graph.rb' - 'lib/puppet/pops/types/ruby_generator.rb' - 'lib/puppet/provider/service/init.rb' - 'lib/puppet/util/rdoc/generators/puppet_generator.rb' Style/ColonMethodDefinition: Enabled: true Style/DefWithParentheses: Enabled: true Style/Dir: Enabled: true Style/DocumentDynamicEvalDefinition: Enabled: true Style/DoubleCopDisableDirective: Enabled: true Style/EachForSimpleLoop: Enabled: true Style/EachWithObject: Enabled: true Style/EmptyBlockParameter: Enabled: true Style/EmptyCaseCondition: Enabled: true Style/EmptyLambdaParameter: Enabled: true Style/EmptyLiteral: Enabled: true Style/EvalWithLocation: Enabled: true Style/EvenOdd: Enabled: true Style/ExpandPathArguments: Enabled: true Style/FetchEnvVar: Enabled: true Style/FileRead: Enabled: true Style/FileWrite: Enabled: true Style/FloatDivision: Enabled: true Style/For: Enabled: true Style/FrozenStringLiteralComment: Enabled: true Style/GlobalStdStream: Enabled: true Style/GlobalVars: Enabled: true Style/HashAsLastArrayItem: Enabled: true Style/HashConversion: Enabled: true # HashEachMethods does not guarantee the receiver is a Hash, so # this will not work since numerous puppet classes define #value # not #each_value Style/HashEachMethods: Enabled: false Style/HashLikeCase: Enabled: true Exclude: - 'lib/puppet/util/command_line/trollop.rb' Style/HashTransformKeys: Enabled: true Style/HashTransformValues: Enabled: true Style/IdenticalConditionalBranches: Enabled: true Style/IfInsideElse: Enabled: true Style/IfUnlessModifierOfIfUnless: Enabled: true Style/IfWithBooleanLiteralBranches: Enabled: true Style/IfWithSemicolon: Enabled: true Style/InfiniteLoop: Enabled: true Style/InverseMethods: Enabled: true Style/KeywordParametersOrder: Enabled: true Style/Lambda: Enabled: true Style/LambdaCall: Enabled: true Style/LineEndConcatenation: Enabled: true Style/MapCompactWithConditionalBlock: Enabled: true Style/MapToHash: Enabled: true Style/MapToSet: Enabled: true puppetlabs-puppet-789f600/.rubocop_todo.yml000066400000000000000000000626371470131746300210140ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config --no-offense-counts --no-auto-gen-timestamp` # using RuboCop version 1.28.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # This cop supports safe auto-correction (--auto-correct). I18n/GetText/DecorateFunctionMessage: Enabled: false # This cop supports safe auto-correction (--auto-correct). I18n/GetText/DecorateString: Enabled: false I18n/GetText/DecorateStringFormattingUsingPercent: Exclude: - 'lib/puppet/provider/user/windows_adsi.rb' - 'lib/puppet/transaction/resource_harness.rb' I18n/RailsI18n/DecorateString: Enabled: false Lint/AmbiguousAssignment: # new in 1.7 Enabled: false Lint/AmbiguousOperatorPrecedence: # new in 1.21 Enabled: false Lint/AmbiguousRange: # new in 1.19 Enabled: false # Configuration parameters: AllowedMethods. # AllowedMethods: enums Lint/ConstantDefinitionInBlock: Exclude: - 'lib/puppet/face/config.rb' - 'lib/puppet/face/help.rb' - 'lib/puppet/face/node/clean.rb' - 'lib/puppet/provider/package/aix.rb' - 'lib/puppet/provider/package/apt.rb' - 'lib/puppet/provider/package/gem.rb' - 'lib/puppet/provider/package/pip.rb' - 'lib/puppet/provider/package/yum.rb' - 'lib/puppet/provider/service/upstart.rb' - 'lib/puppet/provider/user/directoryservice.rb' - 'lib/puppet/type/file.rb' - 'lib/puppet/type/file/source.rb' - 'lib/puppet/type/resources.rb' - 'lib/puppet/type/schedule.rb' - 'lib/puppet/type/tidy.rb' Lint/ConstantOverwrittenInRescue: # new in 1.31 Enabled: false Lint/DeprecatedConstants: # new in 1.8 Enabled: false Lint/DuplicateBranch: # new in 1.3 Enabled: false Lint/DuplicateMagicComment: # new in 1.37 Enabled: false Lint/DuplicateMatchPattern: # new in 1.50 Enabled: false Lint/DuplicateRegexpCharacterClassElement: # new in 1.1 Enabled: false Lint/EmptyBlock: # new in 1.1 Enabled: false Lint/EmptyClass: # new in 1.3 Enabled: false Lint/EmptyInPattern: # new in 1.16 Enabled: false Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21 Enabled: false Lint/ItWithoutArgumentsInBlock: # new in 1.59 Enabled: false Lint/LambdaWithoutLiteralBlock: # new in 1.8 Enabled: false Lint/LiteralAssignmentInCondition: # new in 1.58 Enabled: false Lint/MissingSuper: Enabled: false Lint/MixedCaseRange: # new in 1.53 Enabled: false Lint/NestedMethodDefinition: Exclude: - 'lib/puppet/pops/types/p_binary_type.rb' - 'lib/puppet/pops/types/p_init_type.rb' - 'lib/puppet/pops/types/p_object_type.rb' - 'lib/puppet/pops/types/p_sem_ver_range_type.rb' - 'lib/puppet/pops/types/p_sem_ver_type.rb' - 'lib/puppet/pops/types/p_sensitive_type.rb' - 'lib/puppet/pops/types/p_timespan_type.rb' - 'lib/puppet/pops/types/p_timestamp_type.rb' - 'lib/puppet/pops/types/p_uri_type.rb' - 'lib/puppet/pops/types/types.rb' - 'lib/puppet/type.rb' Lint/NonAtomicFileOperation: # new in 1.31 Enabled: false Lint/NoReturnInBeginEndBlocks: # new in 1.2 Enabled: false Lint/NumberedParameterAssignment: # new in 1.9 Enabled: false Lint/OrAssignmentToConstant: # new in 1.9 Enabled: false Lint/RedundantDirGlobSort: # new in 1.8 Enabled: false Lint/RedundantRegexpQuantifiers: # new in 1.53 Enabled: false # Unsure how the changes in portage.rb from Lint/RedundantSplatExpansion impact # the code Lint/RedundantSplatExpansion: Exclude: - 'lib/puppet/provider/package/portage.rb' Lint/RefinementImportMethods: # new in 1.27 Enabled: false Lint/RequireRangeParentheses: # new in 1.32 Enabled: false Lint/RequireRelativeSelfPath: # new in 1.22 Enabled: false Lint/RescueException: Exclude: - 'ext/windows/service/daemon.rb' - 'lib/puppet/configurer/fact_handler.rb' - 'lib/puppet/generate/type.rb' - 'lib/puppet/settings.rb' - 'lib/puppet/transaction/resource_harness.rb' - 'lib/puppet/util.rb' - 'lib/puppet/util/autoload.rb' - 'lib/puppet/util/command_line/trollop.rb' - 'util/rspec_grouper' # Configuration parameters: AllowComments, AllowNil. Lint/SuppressedException: Exclude: - 'lib/puppet/application/face_base.rb' - 'lib/puppet/ffi/windows/functions.rb' - 'lib/puppet/forge/errors.rb' - 'lib/puppet/functions/each.rb' - 'lib/puppet/functions/filter.rb' - 'lib/puppet/functions/map.rb' - 'lib/puppet/functions/slice.rb' - 'lib/puppet/pops/time/timespan.rb' - 'lib/puppet/pops/types/iterable.rb' - 'lib/puppet/pops/types/p_runtime_type.rb' - 'lib/puppet/util/command_line.rb' - 'lib/puppet/util/execution.rb' - 'util/rspec_grouper' Lint/SymbolConversion: # new in 1.9 Enabled: false Lint/ToEnumArguments: # new in 1.1 Enabled: false # This cop supports safe auto-correction (--auto-correct). Lint/ToJSON: Exclude: - 'lib/puppet/module_tool/metadata.rb' - 'lib/puppet/network/http/error.rb' - 'lib/puppet/pops/serialization/json.rb' Lint/TripleQuotes: # new in 1.9 Enabled: false Lint/UnexpectedBlockArity: # new in 1.5 Enabled: false Lint/UnmodifiedReduceAccumulator: # new in 1.1 Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. Lint/UnusedMethodArgument: Enabled: false Lint/UselessRescue: # new in 1.43 Enabled: false Lint/UselessRuby2Keywords: # new in 1.23 Enabled: false Performance/BlockGivenWithExplicitBlock: # new in 1.9 Enabled: false Performance/CollectionLiteralInLoop: # new in 1.8 Enabled: false Performance/ConstantRegexp: # new in 1.9 Enabled: false # This cop supports safe auto-correction (--auto-correct). Performance/Count: Exclude: - 'lib/puppet/confine/any.rb' - 'lib/puppet/confine/false.rb' - 'lib/puppet/confine/true.rb' - 'lib/puppet/graph/relationship_graph.rb' - 'lib/puppet/provider.rb' # This cop supports unsafe auto-correction (--auto-correct-all). Performance/InefficientHashSearch: Exclude: - 'lib/puppet/face/node/clean.rb' - 'lib/puppet/provider/nameservice/directoryservice.rb' - 'lib/puppet/provider/user/directoryservice.rb' - 'lib/puppet/resource.rb' - 'lib/puppet/util/windows/adsi.rb' Performance/MethodObjectAsBlock: # new in 1.9 Enabled: false # This cop supports safe auto-correction (--auto-correct). Performance/RedundantBlockCall: Exclude: - 'lib/puppet/application.rb' - 'lib/puppet/context.rb' - 'lib/puppet/file_bucket/file.rb' - 'lib/puppet/functions/max.rb' - 'lib/puppet/functions/min.rb' - 'lib/puppet/gettext/stubs.rb' - 'lib/puppet/network/http/api/server/v3.rb' - 'lib/puppet/pal/pal_impl.rb' - 'lib/puppet/pops/adaptable.rb' - 'lib/puppet/pops/lookup/invocation.rb' - 'lib/puppet/pops/model/factory.rb' - 'lib/puppet/util.rb' Performance/RedundantEqualityComparisonBlock: # new in 1.10 Enabled: false # This cop supports unsafe auto-correction (--auto-correct-all). # Configuration parameters: MaxKeyValuePairs. Performance/RedundantMerge: Exclude: - 'lib/puppet/x509/cert_provider.rb' Performance/RedundantSplitRegexpArgument: # new in 1.10 Enabled: false Performance/RedundantStringChars: # new in 1.7 Enabled: false # This cop supports safe auto-correction (--auto-correct). Performance/RegexpMatch: Enabled: false Performance/SortReverse: # new in 1.7 Enabled: false Performance/Squeeze: # new in 1.7 Enabled: false Performance/StringIdentifierArgument: # new in 1.13 Enabled: false Performance/StringInclude: # new in 1.7 Enabled: false Performance/Sum: # new in 1.8 Enabled: false # This cop supports safe auto-correction (--auto-correct). Performance/UnfreezeString: Enabled: false # Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols. # SupportedStyles: inline, group Style/AccessModifierDeclarations: Exclude: - 'lib/puppet/util/suidmanager.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped Style/AccessorGrouping: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: prefer_alias, prefer_alias_method Style/Alias: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: always, conditionals Style/AndOr: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: percent_q, bare_percent Style/BarePercentLiterals: Exclude: - 'lib/puppet/module_tool/metadata.rb' - 'lib/puppet/node/environment.rb' - 'lib/puppet/parameter/package_options.rb' - 'lib/puppet/provider/package/dpkg.rb' - 'lib/puppet/provider/package/gem.rb' - 'lib/puppet/provider/package/rpm.rb' - 'lib/puppet/provider/package/windows/package.rb' - 'lib/puppet/settings.rb' - 'lib/puppet/settings/base_setting.rb' - 'lib/puppet/transaction/event.rb' - 'lib/puppet/util/execution.rb' # This cop supports safe auto-correction (--auto-correct). Style/BisectedAttrAccessor: Exclude: - 'lib/puppet/module.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, IgnoredMethods, AllowBracesOnProceduralOneLiners, BracesRequiredMethods. # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object # FunctionalMethods: let, let!, subject, watch # IgnoredMethods: lambda, proc, it Style/BlockDelimiters: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowOnConstant. Style/CaseEquality: Exclude: - 'lib/puppet/indirector/terminus.rb' - 'lib/puppet/interface/face_collection.rb' - 'lib/puppet/module_tool/installed_modules.rb' - 'lib/puppet/module_tool/shared_behaviors.rb' - 'lib/puppet/util/command_line/puppet_option_parser.rb' - 'lib/puppet/util/log/destination.rb' - 'lib/puppet/util/multi_match.rb' - 'lib/puppet/util/rdoc/generators/puppet_generator.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Enabled: false Style/ClassVars: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/ColonMethodCall: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle, AllowInnerBackticks. # SupportedStyles: backticks, percent_x, mixed Style/CommandLiteral: Exclude: - 'ext/windows/service/daemon.rb' - 'lib/puppet/provider/nameservice/directoryservice.rb' - 'lib/puppet/util/reference.rb' - 'lib/puppet/util/terminal.rb' - 'lib/puppet/util/windows/process.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: Keywords, RequireColon. # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE Style/CommentAnnotation: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/CommentedKeyword: Exclude: - 'lib/puppet/util/command_line/trollop.rb' - 'lib/puppet/util/rdoc/generators/puppet_generator.rb' - 'lib/puppet/util/rdoc/generators/template/puppet/puppet.rb' - 'lib/puppet/util/rpm_compare.rb' - 'lib/puppet/util/windows/service.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle, SingleLineConditionsOnly, IncludeTernaryExpressions. # SupportedStyles: assign_to_condition, assign_inside_condition Style/ConditionalAssignment: Enabled: false # Enabling this would require reworking Puppet's use of DateTime's #rfc2822, #httptime, and _strptime Style/DateTime: Enabled: false # Configuration parameters: AllowedConstants. Style/Documentation: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: allowed_in_returns, forbidden Style/DoubleNegation: Exclude: - 'lib/puppet/application/lookup.rb' - 'lib/puppet/confine/boolean.rb' - 'lib/puppet/http/service/compiler.rb' - 'lib/puppet/parser/functions/fqdn_rand.rb' - 'lib/puppet/pops/evaluator/evaluator_impl.rb' - 'lib/puppet/pops/issue_reporter.rb' - 'lib/puppet/pops/types/p_runtime_type.rb' - 'lib/puppet/provider/package/apt.rb' - 'lib/puppet/resource/status.rb' - 'lib/puppet/type.rb' - 'lib/puppet/util/feature.rb' - 'lib/puppet/util/windows/adsi.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: empty, nil, both Style/EmptyElse: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/EmptyLiteral: Exclude: - 'lib/puppet/parser/scope.rb' - 'lib/puppet/pops/puppet_stack.rb' - 'lib/puppet/pops/visitor.rb' - 'lib/puppet/provider/package/portupgrade.rb' - 'lib/puppet/provider/service/launchd.rb' - 'lib/puppet/provider/user/directoryservice.rb' - 'lib/puppet/type.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: compact, expanded Style/EmptyMethod: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/Encoding: Exclude: - 'lib/puppet/face/module/install.rb' - 'lib/puppet/face/module/list.rb' - 'lib/puppet/face/module/upgrade.rb' - 'lib/puppet/ffi/windows/structs.rb' - 'lib/puppet/interface/action.rb' - 'lib/puppet/module_tool.rb' - 'lib/puppet/type.rb' - 'lib/puppet/type/file.rb' - 'lib/puppet/type/package.rb' - 'lib/puppet/util/windows/service.rb' # This cop supports safe auto-correction (--auto-correct). Style/ExplicitBlockArgument: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: format, sprintf, percent Style/FormatString: Enabled: false # Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, IgnoredMethods. # SupportedStyles: annotated, template, unannotated Style/FormatStringToken: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Applying the safe auto-correct results in util_spec failures. Style/GlobalStdStream: Exclude: - 'lib/puppet/application/apply.rb' - 'lib/puppet/application/script.rb' - 'lib/puppet/face/epp.rb' - 'lib/puppet/face/parser.rb' - 'lib/puppet/util.rb' - 'lib/puppet/util/command_line.rb' - 'lib/puppet/util/windows/daemon.rb' # Configuration parameters: AllowedVariables. Style/GlobalVars: Exclude: - 'lib/puppet/external/dot.rb' - 'lib/puppet/test/test_helper.rb' - 'lib/puppet/util/logging.rb' # Configuration parameters: MinBodyLength, AllowConsecutiveConditionals. Style/GuardClause: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys # SupportedShorthandSyntax: always, never, either Style/HashSyntax: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/IfUnlessModifier: Enabled: false # This cop supports safe auto-correction (--auto-correct). # The auto-correct function introduces testing errors in the evaluating_parser_spec Style/IfWithSemicolon: Exclude: - 'lib/puppet/pops/parser/evaluating_parser.rb' # This cop requires significant changes to testing, will require its own effort Style/ImplicitRuntimeError: Enabled: false # This cop supports unsafe auto-correction (--auto-correct-all). # Configuration parameters: InverseMethods, InverseBlocks. Style/InverseMethods: Exclude: - 'lib/puppet/face/catalog/select.rb' - 'lib/puppet/graph/relationship_graph.rb' - 'lib/puppet/parser/compiler.rb' - 'lib/puppet/pops/loader/loader_paths.rb' - 'lib/puppet/pops/types/ruby_generator.rb' - 'lib/puppet/pops/validation.rb' - 'lib/puppet/pops/validation/checker4_0.rb' - 'lib/puppet/provider/package/pkg.rb' - 'lib/puppet/provider/user/user_role_add.rb' - 'lib/puppet/reference/providers.rb' - 'lib/puppet/type/file.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: IgnoredMethods. Style/MethodCallWithoutArgsParentheses: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline Style/MethodDefParentheses: Exclude: - 'lib/puppet/pops/evaluator/evaluator_impl.rb' - 'lib/puppet/pops/evaluator/relationship_operator.rb' - 'lib/puppet/pops/issues.rb' - 'lib/puppet/pops/label_provider.rb' - 'lib/puppet/pops/model/factory.rb' - 'lib/puppet/pops/model/model_label_provider.rb' - 'lib/puppet/pops/model/model_tree_dumper.rb' - 'lib/puppet/pops/model/tree_dumper.rb' - 'lib/puppet/pops/parser/interpolation_support.rb' - 'lib/puppet/pops/parser/parser_support.rb' - 'lib/puppet/pops/utils.rb' - 'lib/puppet/pops/validation.rb' - 'lib/puppet/pops/validation/validator_factory_4_0.rb' - 'lib/puppet/util/command_line/trollop.rb' Style/MissingRespondToMissing: Exclude: - 'lib/puppet/module_tool/metadata.rb' - 'lib/puppet/parser/scope.rb' - 'lib/puppet/settings/alias_setting.rb' - 'lib/puppet/util/command_line/trollop.rb' - 'lib/puppet/util/feature.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped Style/MixinGrouping: Exclude: - 'lib/puppet/util/rdoc/generators/puppet_generator.rb' Style/MultilineBlockChain: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/MultilineIfModifier: Exclude: - 'lib/puppet/face/config.rb' - 'lib/puppet/module_tool/applications/installer.rb' - 'lib/puppet/module_tool/shared_behaviors.rb' - 'lib/puppet/network/http/api/indirected_routes.rb' - 'lib/puppet/pops/evaluator/access_operator.rb' - 'lib/puppet/pops/loader/task_instantiator.rb' - 'lib/puppet/pops/model/model_tree_dumper.rb' - 'lib/puppet/provider/package/windows.rb' - 'lib/puppet/provider/service/upstart.rb' - 'lib/puppet/resource/catalog.rb' - 'lib/puppet/type/file/content.rb' - 'lib/puppet/type/user.rb' - 'lib/puppet/util/execution.rb' - 'lib/puppet/util/windows/com.rb' # This cop supports safe auto-correction (--auto-correct). Style/MultilineIfThen: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: keyword, braces Style/MultilineMemoization: Exclude: - 'lib/puppet/application.rb' - 'lib/puppet/pops/types/types.rb' - 'lib/puppet/type.rb' # This cop supports safe auto-correction (--auto-correct). Style/MultilineTernaryOperator: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/MultilineWhenThen: Exclude: - 'lib/puppet/graph/simple_graph.rb' - 'lib/puppet/interface/documentation.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowMethodComparison. Style/MultipleComparison: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: literals, strict Style/MutableConstant: Enabled: false # This cop supports unsafe auto-correction (--auto-correct-all). # Configuration parameters: EnforcedStyle, IgnoredMethods. # SupportedStyles: predicate, comparison Style/NumericPredicate: Enabled: false # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: implicit, explicit Style/RescueStandardError: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle. # SupportedStyles: only_raise, only_fail, semantic Style/SignalException: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowIfMethodIsEmpty. Style/SingleLineMethods: Exclude: - 'lib/puppet/ffi/windows/api_types.rb' - 'lib/puppet/file_system/memory_file.rb' - 'lib/puppet/graph/simple_graph.rb' - 'lib/puppet/interface/action.rb' - 'lib/puppet/parser/resource.rb' - 'lib/puppet/pops/model/factory.rb' - 'lib/puppet/pops/model/model_label_provider.rb' - 'lib/puppet/pops/types/type_formatter.rb' - 'lib/puppet/provider/nameservice/directoryservice.rb' - 'lib/puppet/provider/service/freebsd.rb' - 'lib/puppet/type.rb' - 'lib/puppet/util/command_line/trollop.rb' - 'lib/puppet/util/metaid.rb' - 'lib/puppet/util/windows/com.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowModifier. Style/SoleNestedConditional: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/StderrPuts: Exclude: - 'bin/puppet' - 'lib/puppet/application/agent.rb' - 'lib/puppet/application/apply.rb' - 'lib/puppet/application/describe.rb' - 'lib/puppet/application/device.rb' - 'lib/puppet/application/face_base.rb' - 'lib/puppet/application/filebucket.rb' - 'lib/puppet/application/script.rb' - 'lib/puppet/face/config.rb' - 'lib/puppet/reference/type.rb' - 'lib/puppet/util.rb' - 'lib/puppet/util/command_line/trollop.rb' - 'lib/puppet/util/rdoc/generators/puppet_generator.rb' - 'lib/puppet/util/reference.rb' # This cop supports unsafe auto-correction (--auto-correct-all). # Configuration parameters: Mode. Style/StringConcatenation: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. # SupportedStyles: single_quotes, double_quotes Style/StringLiterals: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle, MinSize. # SupportedStyles: percent, brackets Style/SymbolArray: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyle, AllowSafeAssignment. # SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex Style/TernaryParentheses: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: EnforcedStyleForMultiline. # SupportedStylesForMultiline: comma, consistent_comma, no_comma Style/TrailingCommaInHashLiteral: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowNamedUnderscoreVariables. Style/TrailingUnderscoreVariable: Exclude: - 'lib/puppet/indirector/file_server.rb' - 'lib/puppet/module/plan.rb' - 'lib/puppet/pops/evaluator/closure.rb' - 'lib/puppet/pops/parser/parser_support.rb' - 'lib/puppet/provider/group/windows_adsi.rb' - 'lib/puppet/provider/package/zypper.rb' - 'lib/puppet/provider/service/launchd.rb' - 'lib/puppet/provider/user/pw.rb' - 'lib/puppet/provider/user/windows_adsi.rb' - 'lib/puppet/resource/type.rb' - 'lib/puppet/util/windows/registry.rb' # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods. # AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym Style/TrivialAccessors: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/WhenThen: Enabled: false # This cop supports safe auto-correction (--auto-correct). Style/WhileUntilModifier: Exclude: - 'lib/puppet/parser/scope.rb' - 'lib/puppet/pops/parser/interpolation_support.rb' - 'lib/puppet/pops/parser/locator.rb' - 'lib/puppet/pops/parser/pn_parser.rb' - 'lib/puppet/pops/types/p_object_type.rb' - 'lib/puppet/util/windows/process.rb' # This cop supports unsafe auto-correction (--auto-correct-all). # Configuration parameters: EnforcedStyle. # SupportedStyles: forbid_for_all_comparison_operators, forbid_for_equality_operators_only, require_for_all_comparison_operators, require_for_equality_operators_only Style/YodaCondition: Enabled: false # This cop supports unsafe auto-correction (--auto-correct-all). Style/ZeroLengthPredicate: Enabled: false # This cop supports safe auto-correction (--auto-correct). # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, IgnoredPatterns. # URISchemes: http, https Layout/LineLength: Max: 582 puppetlabs-puppet-789f600/.yardopts000066400000000000000000000005061470131746300173460ustar00rootroot00000000000000--protected --private --verbose --markup markdown --readme README.md --tag status --transitive-tag status --tag comment --hide-tag comment --tag dsl:"DSL" --no-transitive-tag api --template-path yardoc/templates --files CO*.md,api/**/*.md --api public --api private --hide-void-return --exclude lib/puppet/vendor/ lib/**/*.rb puppetlabs-puppet-789f600/CODEOWNERS000066400000000000000000000004161470131746300170730ustar00rootroot00000000000000# defaults * @puppetlabs/phoenix # PAL /lib/puppet/pal @puppetlabs/bolt # puppet module /lib/puppet/application/module.rb @puppetlabs/modules /lib/puppet/face/module @puppetlabs/modules /lib/puppet/forge @puppetlabs/modules /lib/puppet/module_tool @puppetlabs/modules puppetlabs-puppet-789f600/CODE_OF_CONDUCT.md000066400000000000000000000076011470131746300203020ustar00rootroot00000000000000# Community Guidelines and Code of Conduct We want to keep the Puppet communities awesome, and we need your help to keep it that way. While we have specific guidelines for various tools (see links below), in general, you should: * **Be nice**: Be courteous, respectful and polite to fellow community members. No offensive comments related to gender, gender identity or expression, sexual orientation, disability, physical appearance, body size, race, religion; no sexual images in public spaces, real or implied violence, intimidation, oppression, stalking, following, harassing photography or recording, sustained disruption of talks or other events, inappropriate physical contact, doxxing, or unwelcome sexual attention will be tolerated. We like nice people way better than mean ones! * **Encourage diversity and participation**: Make everyone in our community feel welcome, regardless of their background, and do everything possible to encourage participation in our community. * **Focus on constructive criticisms**: When offering suggestions, whether in online discussions or as comments on a pull request, you should always use welcoming and inclusive language. Be respectful of differing viewpoints and the fact that others may not have the same experiences you do. Offer suggestions for improvement, rather than focusing on mistakes. When others critique your work or ideas, gracefully accept the criticisms and default to assuming good intentions. * **Keep it legal**: Basically, don't get us in trouble. Share only content that you own, do not share private or sensitive information, and don't break the law. * **Stay on topic**: Keep conversation in a thread on topic, whether that's a pull request or a Slack conversation or anything else. Make sure that you are posting to the correct channel and remember that nobody likes spam. ## Guideline violations --- 3 strikes method The point of this section is not to find opportunities to punish people, but we do need a fair way to deal with people who do harm to our community. Extreme violations of a threatening, abusive, destructive, or illegal nature will be addressed immediately and are not subject to 3 strikes. * First occurrence: We'll give you a friendly, but public, reminder that the behavior is inappropriate according to our guidelines. * Second occurrence: We'll send you a private message with a warning that any additional violations will result in removal from the community. * Third occurrence: Depending on the violation, we might need to delete or ban your account. Notes: * Obvious spammers are banned on first occurrence. If we don’t do this, we’ll have spam all over the place. * Violations are forgiven after 6 months of good behavior, and we won’t hold a grudge. * People who are committing minor formatting / style infractions will get some education, rather than hammering them in the 3 strikes process. Contact conduct@puppet.com to report abuse or appeal violations. This email list goes to Kara Sowles (kara at puppet.com) and Katie Abbott (katie dot abbott at puppet.com). In the case of appeals, we know that mistakes happen, and we’ll work with you to come up with a fair solution if there has been a misunderstanding. ## Full text See our [full community guidelines](https://puppet.com/community/community-guidelines), covering Slack, IRC, events and other forms of community participation. ## Credits Credit to [01.org](https://01.org/community/participation-guidelines) and [meego.com](http://wiki.meego.com/Community_guidelines), since they formed the starting point for many of these guidelines. The Event Code of Conduct is based on the [example policy from the Geek Feminism wiki](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment), created by the Ada Initiative and other volunteers. The [PyCon Code of Conduct](https://github.com/python/pycon-code-of-conduct) also served as inspiration. puppetlabs-puppet-789f600/Gemfile000066400000000000000000000065421470131746300170010ustar00rootroot00000000000000source ENV['GEM_SOURCE'] || "https://rubygems.org" gemspec def location_for(place, fake_version = nil) if place.is_a?(String) && place =~ /^((?:git[:@]|https:)[^#]*)#(.*)/ [fake_version, { git: $1, branch: $2, require: false }].compact elsif place.is_a?(String) && place =~ /^file:\/\/(.*)/ ['>= 0', { path: File.expand_path($1), require: false }] else [place, { require: false }] end end # Make sure these gem requirements are in sync with the gempspec. Specifically, # the runtime_dependencies in puppet.gemspec match the runtime dependencies here # (like facter, semantic_puppet, and puppet-resource_api) gem "facter", *location_for(ENV['FACTER_LOCATION'] || ["~> 4.3"]) gem "semantic_puppet", *location_for(ENV['SEMANTIC_PUPPET_LOCATION'] || ["~> 1.0"]) gem "puppet-resource_api", *location_for(ENV['RESOURCE_API_LOCATION'] || ["~> 1.5"]) group(:features) do gem 'diff-lcs', '~> 1.3', require: false gem "hiera", *location_for(ENV['HIERA_LOCATION']) if ENV.has_key?('HIERA_LOCATION') gem 'hiera-eyaml', *location_for(ENV['HIERA_EYAML_LOCATION']) gem 'hocon', '~> 1.0', require: false # requires native libshadow headers/libs #gem 'ruby-shadow', '~> 2.5', require: false, platforms: [:ruby] gem 'minitar', '~> 0.9', require: false gem 'msgpack', '~> 1.2', require: false gem 'rdoc', ['~> 6.0', '< 6.4.0'], require: false, platforms: [:ruby] # requires native augeas headers/libs # gem 'ruby-augeas', require: false, platforms: [:ruby] # requires native ldap headers/libs # gem 'ruby-ldap', '~> 0.9', require: false, platforms: [:ruby] gem 'puppetserver-ca', '~> 2.0', require: false gem 'syslog', '~> 0.1.1', require: false, platforms: [:ruby] gem 'CFPropertyList', ['>= 3.0.6', '< 4'], require: false end group(:test) do # 1.16.0 - 1.16.2 are broken on Windows gem 'ffi', '>= 1.15.5', '< 1.17.0', '!= 1.16.0', '!= 1.16.1', '!= 1.16.2', require: false gem "json-schema", "~> 2.0", require: false gem "racc", "1.5.2", require: false gem "rake", *location_for(ENV['RAKE_LOCATION'] || '~> 13.0') gem "rspec", "~> 3.1", require: false gem "rspec-expectations", ["~> 3.9", "!= 3.9.3"] gem "rspec-its", "~> 1.1", require: false gem 'vcr', '~> 6.1', require: false gem 'webmock', '~> 3.0', require: false gem 'webrick', '~> 1.7', require: false gem 'yard', require: false gem 'rubocop', '~> 1.0', require: false, platforms: [:ruby] gem 'rubocop-i18n', '~> 3.0', require: false, platforms: [:ruby] gem 'rubocop-performance', '~> 1.0', require: false, platforms: [:ruby] gem 'rubocop-rake', '~> 0.6', require: false, platforms: [:ruby] gem 'rubocop-rspec', '~> 2.0', require: false, platforms: [:ruby] end group(:development, optional: true) do gem 'memory_profiler', require: false, platforms: [:mri] gem 'pry', require: false, platforms: [:ruby] if RUBY_PLATFORM != 'java' gem 'ruby-prof', '>= 0.16.0', require: false end end group(:packaging) do gem 'packaging', *location_for(ENV['PACKAGING_LOCATION'] || '~> 0.99') end group(:documentation, optional: true) do gem 'gettext-setup', '~> 1.0', require: false, platforms: [:ruby] gem 'ronn', '~> 0.7.3', require: false, platforms: [:ruby] gem 'puppet-strings', require: false, platforms: [:ruby] gem 'pandoc-ruby', require: false, platforms: [:ruby] end if File.exist? "#{__FILE__}.local" eval(File.read("#{__FILE__}.local"), binding) end # vim:filetype=ruby puppetlabs-puppet-789f600/Guardfile.example000066400000000000000000000043421470131746300207610ustar00rootroot00000000000000# More info at https://github.com/guard/guard#readme # You'll need to make sure Guard, and any of its plugins are in your Gemfile.local # # Example: # # Automatically run tests on file changes # gem 'guard', require: false # gem 'guard-rspec', require: false # gem 'guard-bundler', require: false # gem 'terminal-notifier-guard', require: false # # After running `bundle install`, you can run Guard via `bundle exec guard` # from the top of the repository checkout. notification(:terminal_notifier, app_name: "Puppet ::", group: `pwd`.chomp) if `uname` =~ /Darwin/ ## Uncomment and set this to only include directories you want to watch # directories %w(app lib config test spec features) \ # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")} ## Note: if you are using the `directories` clause above and you are not ## watching the project directory ('.'), then you will want to move ## the Guardfile to a watched dir and symlink it back, e.g. # # $ mkdir config # $ mv Guardfile config/ # $ ln -s config/Guardfile . # # and, you'll have to watch "config/Guardfile" instead of "Guardfile" guard :bundler do require 'guard/bundler' require 'guard/bundler/verify' helper = Guard::Bundler::Verify.new files = ['Gemfile', 'Gemfile.local'] files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) } # Assume files are symlinked from somewhere files.each { |file| watch(helper.real_path(file)) } end def file2specs(match) file = match[0] puts "Lib file changed: #{file.inspect}" %w{spec/unit spec/integration}.collect { |d| file.sub('lib/puppet', d).sub(".rb", "_spec.rb") }.find_all { |f| File.exist?(f) } end rspec_options = { cmd: "bundle exec rspec", run_all: { cmd: "bundle exec parallel_rspec -o '--format progress ", cmd_additional_args: "'" }, all_after_pass: false } guard :rspec, rspec_options do require "guard/rspec/dsl" dsl = Guard::RSpec::Dsl.new(self) # Feel free to open issues for suggestions and improvements # RSpec files rspec = dsl.rspec watch(rspec.spec_helper) { rspec.spec_dir } watch(rspec.spec_support) { rspec.spec_dir } watch(rspec.spec_files) # Ruby files ruby = dsl.ruby watch(ruby.lib_files) { |f| file2specs(f) } end puppetlabs-puppet-789f600/LICENSE000066400000000000000000000261361470131746300165140ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. puppetlabs-puppet-789f600/README.md000066400000000000000000000065231470131746300167640ustar00rootroot00000000000000# Puppet ![RSpec tests](https://github.com/puppetlabs/puppet/workflows/RSpec%20tests/badge.svg) [![Gem Version](https://badge.fury.io/rb/puppet.svg)](https://badge.fury.io/rb/puppet) [![Inline docs](https://inch-ci.org/github/puppetlabs/puppet.svg)](https://inch-ci.org/github/puppetlabs/puppet) Puppet, an automated administrative engine for your Linux, Unix, and Windows systems, performs administrative tasks (such as adding users, installing packages, and updating server configurations) based on a centralized specification. ## Documentation Documentation for Puppet and related projects can be found online at the [Puppet Docs site](https://puppet.com/docs). ### HTTP API [HTTP API Index](https://puppet.com/docs/puppet/latest/http_api/http_api_index.html) ## Installation The best way to run Puppet is with [Puppet Enterprise (PE)](https://puppet.com/products/puppet-enterprise/), which also includes orchestration features, a web console, and professional support. The PE documentation is [available here.](https://puppet.com/docs/pe/latest) To install an open source release of Puppet, [see the installation guide on the docs site.](https://puppet.com/docs/puppet/latest/installing_and_upgrading.html) If you need to run Puppet from source as a tester or developer, see the [Quick Start to Developing on Puppet](docs/quickstart.md) guide. ## Developing and Contributing We'd love to get contributions from you! For a quick guide to getting your system setup for developing, take a look at our [Quickstart Guide](https://github.com/puppetlabs/puppet/blob/main/docs/quickstart.md). Once you are up and running, take a look at the [Contribution Documents](https://github.com/puppetlabs/.github/blob/main/CONTRIBUTING.md) to see how to get your changes merged in. For more complete docs on developing with Puppet, take a look at the rest of the [developer documents](https://github.com/puppetlabs/puppet/blob/main/docs/index.md). ## Licensing See [LICENSE](https://github.com/puppetlabs/puppet/blob/main/LICENSE) file. Puppet is licensed by Puppet, Inc. under the Apache license. Puppet, Inc. can be contacted at: info@puppet.com ## Support Please log issues in this project's [GitHub Issues](https://github.com/puppetlabs/puppet/issues). A [mailing list](https://groups.google.com/forum/?fromgroups#!forum/puppet-users) is available for asking questions and getting help from others, or if you prefer chat, we also have a [Puppet Community slack.](https://puppetcommunity.slack.com/) We use semantic version numbers for our releases and recommend that users stay as up-to-date as possible by upgrading to patch releases and minor releases as they become available. Bug fixes and ongoing development will occur in minor releases for the current major version. Security fixes will be backported to a previous major version on a best-effort basis, until the previous major version is no longer maintained. For example: If a security vulnerability is discovered in Puppet 8.1.1, we would fix it in the 8 series, most likely as 8.1.2. Maintainers would then make a best effort to backport that fix onto the latest Puppet 7 release. Long-term support, including security patches and bug fixes, is available for commercial customers. Please see the following page for more details: [Puppet Enterprise Support Lifecycle](https://puppet.com/docs/puppet-enterprise/product-support-lifecycle/) puppetlabs-puppet-789f600/Rakefile000066400000000000000000000067411470131746300171540ustar00rootroot00000000000000# frozen_string_literal: true require 'open3' require 'rake' require 'rubygems' require 'rubygems/package_task' if Rake.application.top_level_tasks.grep(/^(pl:|package:)/).any? begin require 'packaging' Pkg::Util::RakeUtils.load_packaging_tasks rescue LoadError => e puts "Error loading packaging rake tasks: #{e}" end end namespace :package do task :bootstrap do puts 'Bootstrap is no longer needed, using packaging-as-a-gem' end task :implode do puts 'Implode is no longer needed, using packaging-as-a-gem' end end task :default do sh %{rake -T} end namespace :pl_ci do desc 'Build puppet gems' task :gem_build, [:gemspec] do |t, args| args.with_defaults(gemspec: 'puppet.gemspec') stdout, stderr, status = Open3.capture3(<<~END) gem build #{args.gemspec} --platform x86-mingw32 && \ gem build #{args.gemspec} --platform x64-mingw32 && \ gem build #{args.gemspec} --platform universal-darwin && \ gem build #{args.gemspec} END if !status.exitstatus.zero? puts "Error building #{args.gemspec}\n#{stdout} \n#{stderr}" exit(1) else puts stdout end end desc 'build the nightly puppet gems' task :nightly_gem_build do # this is taken from `rake package:nightly_gem` extended_dot_version = %x{git describe --tags --dirty --abbrev=7}.chomp.tr('-', '.') # we must create tempfile in the same directory as puppetg.gemspec, since # it uses __dir__ to determine which files to include require 'tempfile' Tempfile.create('gemspec', __dir__) do |dst| File.open('puppet.gemspec', 'r') do |src| src.readlines.each do |line| if line.match?(/version\s*=\s*['"][0-9.]+['"]/) line = "spec.version = '#{extended_dot_version}'" end dst.puts line end end dst.flush Rake::Task['pl_ci:gem_build'].invoke(dst.path) end end end task :spec do ENV["LOG_SPEC_ORDER"] = "true" sh %{rspec #{ENV['TEST'] || ENV['TESTS'] || 'spec'}} end desc 'run static analysis with rubocop' task(:rubocop) do require 'rubocop' cli = RuboCop::CLI.new exit_code = cli.run(%w(--display-cop-names --format simple)) raise "RuboCop detected offenses" if exit_code != 0 end desc "verify that changed files are clean of Ruby warnings" task(:warnings) do # This rake task looks at all files modified in this branch. commit_range = 'HEAD^..HEAD' ruby_files_ok = true puts "Checking modified files #{commit_range}" %x{git diff --diff-filter=ACM --name-only #{commit_range}}.each_line do |modified_file| modified_file.chomp! # Skip racc generated file as it can have many warnings that cannot be manually fixed next if modified_file.end_with?("pops/parser/eparser.rb") next if modified_file.start_with?('spec/fixtures/', 'acceptance/fixtures/') || File.extname(modified_file) != '.rb' puts modified_file stdout, stderr, _ = Open3.capture3("ruby -wc \"#{modified_file}\"") unless stderr.empty? ruby_files_ok = false puts stderr end puts stdout end raise "One or more ruby files contain warnings." unless ruby_files_ok end if Rake.application.top_level_tasks.grep(/^gettext:/).any? begin spec = Gem::Specification.find_by_name 'gettext-setup' load "#{spec.gem_dir}/lib/tasks/gettext.rake" GettextSetup.initialize(File.absolute_path('locales', File.dirname(__FILE__))) rescue LoadError abort("Run `bundle install --with documentation` to install the `gettext-setup` gem.") end end puppetlabs-puppet-789f600/acceptance/000077500000000000000000000000001470131746300175655ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/.beaker.yml000066400000000000000000000006331470131746300216210ustar00rootroot00000000000000--- ssh: keys: - id_rsa_acceptance - ~/.ssh/id_rsa-acceptance xml: true timesync: false repo_proxy: true add_el_extras: false 'master-start-curl-retries': 30 log_level: debug preserve_hosts: onfail helper: ./lib/helper.rb options_file: ./config/aio/options.rb puppetlabs-puppet-789f600/acceptance/.gitignore000066400000000000000000000002621470131746300215550ustar00rootroot00000000000000.vagrant local_options.rb pl-puppet-build.repo pl-puppet-repos.rpm repos.tar repo-configs log id_rsa-acceptance id_rsa-acceptance.pub preserved_config.yaml merged_options.rb tmp puppetlabs-puppet-789f600/acceptance/Gemfile000066400000000000000000000027501470131746300210640ustar00rootroot00000000000000# Specifies a gem mirror; duplicated in acceptance setup # to ensure a similar environment on acceptance hosts. source ENV['GEM_SOURCE'] || 'https://rubygems.org' def location_for(place, fake_version = nil) if place.is_a?(String) && place =~ /^((?:git[:@]|https:)[^#]*)#(.*)/ [fake_version, { :git => $1, :branch => $2, :require => false }].compact elsif place.is_a?(String) && place =~ /^file:\/\/(.*)/ ['>= 0', { :path => File.expand_path($1), :require => false }] else [place, { :require => false }] end end gem "beaker", *location_for(ENV['BEAKER_VERSION'] || '~> 6.0') gem "beaker-puppet", *location_for(ENV['BEAKER_PUPPET_VERSION' || "~> 4.0"]) gem "beaker-hostgenerator", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION'] || "~> 2") gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || "~> 1.0") gem "beaker-vagrant", *location_for(ENV['BEAKER_VAGRANT_VERSION'] || "~> 0") gem "beaker-vmpooler", *location_for(ENV['BEAKER_VMPOOLER_VERSION'] || "~> 1.3") gem "beaker-vcloud", *location_for(ENV['BEAKER_VCLOUD_VERSION'] || "~> 1.0") gem "beaker-docker", *location_for(ENV['BEAKER_DOCKER_VERSION'] || "~> 0.5") gem "beaker-gke", *location_for(ENV['BEAKER_GKE_VERSION'] || "~> 0.0.3") gem "rake", ">= 12.3.3" gem "httparty", :require => false gem 'uuidtools', :require => false group(:test) do gem "rspec", "~> 2.14.0", :require => false gem "mocha", "~> 0.10.5", :require => false end if File.exist? "#{__FILE__}.local" eval(File.read("#{__FILE__}.local"), binding) end puppetlabs-puppet-789f600/acceptance/README.md000066400000000000000000000522451470131746300210540ustar00rootroot00000000000000# Running Puppet Acceptance Tests ## Table of Contents * [Setup](#setup) * [Quick Start](#quick-start) * [Configuration](#configuration) * [Running Tests](#running-tests) * [Writing Tests](#writing-tests) * [Getting Help](#getting-help) ------------- An important aside: Currently running acceptance tests that contain a specific change is challenging unless you have access to infrastructure internal to the Puppet, Inc. network. This is a known issue, and we are working to make this a better experience for our community. ------------- ## Setup ### Prerequisites * git * ruby * [bundler][] * a local clone of the puppet repo All command examples in this readme assume you are working in the same directory this README is in, `puppet/acceptance`. ### Installation All of the dependencies you need to run and develop tests are defined in `Gemfile`. To install them, run `bundle install --path .bundle/gems`. This command, as well all the command examples in this README, assume you are working in the acceptance directory. If you ever have issues with your runtime dependencies, you can update them with `bundle update` or start over fresh with `rm -rf .bundle/gems; bundle install --path .bundle/gems`. To ensure installation was successful, you can run `bundle exec rake -T`. This should return something along these lines: ``` $ bundle exec rake -T rake ci:help # Print usage information rake ci:test:aio # Run the acceptance tests using puppet-agent (AI... rake ci:test:gem # Run the acceptance tests against puppet gem on ... rake ci:test:git # Run the acceptance tests against a git checkout rake ci:test:quick # Run a limited but representative subset of acce... rake clean # Remove any temporary products rake clobber # Remove any generated files ``` To get a detailed description of all of these tasks, run `bundle exec rake -D`. ------------- ## Quick Start ### For community members Currently, there isn't a good way for community members to run acceptance tests. This is a known problem. We currently have multiple avenues we are exploring to make running puppet acceptance tests easier for our community. In the meantime, we apologize for the inconvenience. ### For Puppet, Inc. employees If you have access to infrastructure internal to the Puppet, Inc. network, then the quickest way to get acceptance tests running is with vmpooler. To test changes that are available on a branch on github.com: ``` bundle exec rake ci:test:git OPTIONS='--preserve-hosts=always' SHA=ticket/6.0.x/ticketed-work-description RUNTIME_BRANCH=6.0.x FORK=melissa TESTS='tests/path/to/test.rb,tests/other/test.rb' ``` Where `SHA` is the branch name, `RUNTIME_BRANCH` is the agent version stream, and `FORK` is the github fork where the branch lives. To test changes that are available in a puppet-agent package on builds.delivery.puppetlabs.net: ``` bundle exec rake ci:test:aio OPTIONS='--preserve-hosts=always' SHA=9124b4e81ec0ac6394d3edc67d4ab71866869fd7 TESTS='tests/path/to/test.rb,tests/other/test.rb' ``` `SHA` is a sha or tag that exists on builds.delivery.puppetlabs.net/puppet-agent To rerun a test on the hosts that have already been provisioned, use beaker subcommands: ``` bundle exec beaker exec tests/path/to/test.rb,tests/other/test.rb ``` Always clean up after yourself when you are done: ``` bundle exec beaker destroy ``` This will remove any provisioned hosts. Only run this once you are done with the hosts that have been checked out and provisioned for a given run. ------------- ## Configuration ### Environment Variables A detailed description of the available environment variables can be found by running `bundle exec rake ci:help`. This will print a list of both required and optional environment variables with short descriptions on how they are used. Please review all of these options as they will impact how your test servers are provisioned. This rake task is the most up to date source for this information. Please read through the available variables, their defaults, and what they do. They may impact your acceptance run in ways you do not expect. ### Customizing Test Targets If you are using the vmpooler hypervisor internal to Puppet, Inc. infrastructure, you can customize the platforms to test on using the `HOSTS` environment variable. You'll set the `HOSTS` environment variable to the host string you want to test, such as `HOSTS=redhat7-64ma-windows2012r2-64a`. For a list of available `HOSTS` platforms and their exact naming structures, check the keys listed in [beaker hostgenerator](https://github.com/puppetlabs/beaker-hostgenerator/blob/master/lib/beaker-hostgenerator/data.rb). Generally, this string will be in the format `{platform}{version}-{architecture}{role/s}`. You will most often use either the agent (a) or master (m) role, but you can find a list of available roles in [beaker hostgenerator](https://github.com/puppetlabs/beaker-hostgenerator/blob/master/lib/beaker-hostgenerator/roles.rb). Multiple hosts in the string are separated with a dash(`-`). You must have at least one agent and at least one master. Be careful not to confuse the different host string formats. We have different tools that expect the host string to be in different forms. For example, `packaging_platform` is specific to how [Vanagon](https://github.com/puppetlabs/vanagon) parses that string. ### The Hosts File The rake tasks that run acceptance will by default create a hosts file and populate it using [beaker-hostgenerator][] using either the `HOSTS` environment variable or the default host string (currently `redhat7-64ma-windows2012r2-64a`). The automation assumes you are using the vmpooler hypervisor and a vmpooler instance that is only available to Puppet, Inc. employees. If you want to customize the hypervisor or the vmpooler instance, you'll need to generate your own hosts file. You must pass in a valid host string to the `beaker-hostgenerator` command. See [Customizing Test Targets](#customizing-test-targets) for more information on how to construct a valid host string. To customize the hypervisor, pass in `--hypervisor {hypervisor name}`. To set the vmpooler instance, use `--global-config pooling_api={vmpooler uri}`. Only the vmpooler hypervisor uses the pooling_api key. The host string that is passed in is the same that you would use with the `HOSTS` environment variable. See [Customizing Test Targets](#customizing-test-targets) on how to format this string. To have the automation recognize and use your custom hosts file, you'll need to set the `HOSTS` environment variable to the hosts file. In the above example, we called this file `hosts.yaml`, so we will set `HOSTS` to `hosts.yaml` when running all future beaker commands or rake tasks to run acceptance tests. For example, if you were to run this command: ``` bundle exec beaker-hostgenerator redhat7-64ma-windows2012r2-64a --disable-default-role --osinfo-version 1 --hypervisor vmpooler --global-config pooling_api=http://customvmpooler/ > hosts.yaml ``` You would generate a file called `hosts.yaml` that contains something like this: ``` --- HOSTS: redhat7-64-1: platform: el-7-x86_64 packaging_platform: el-7-x86_64 template: redhat-7-x86_64 hypervisor: vmpooler roles: - master - agent windows2012r2-64-1: platform: windows-2012r2-64 packaging_platform: windows-2012-x64 ruby_arch: x64 template: win-2012r2-x86_64 hypervisor: vmpooler roles: - agent CONFIG: nfs_server: none consoleport: 443 pooling_api: http://customvmpooler/ ``` We can then run the acceptance tests with: `bundle exec rake ci:test:aio HOSTS=hosts.yaml SHA={sha}` ### Hypervisor Options The hypervisor dictates where you will be running the acceptance tests. The beaker hypervisors take care of basic host setup so that you will have a consistent host environment across every test run. You can find more details on the different hypervisor options in [the beaker repo](https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/README.md). Here, we will focus on vmpooler and docker, as those are the two we use most often internally. If you use a hypervisor other than abs, vagrant, vmpooler, or docker, you'll have to add the gem to that hypervisor to `Gemfile.local` and run `bundle update` to install the new gems. You also have the ability to run tests on a static host. #### VMPooler [VMPooler](https://github.com/puppetlabs/vmpooler) is the default hypervisor we use. This is only available to Puppet, Inc. employees as it uses internal infrastructure. If you have access to a similar setup, then you are welcome to use this option with a few values changed. If you are using the Puppet internal vmpooler, then you can simply run the acceptance rake tasks. See [Customizing Test Targets](#customizing-test-targets) about how to use the `HOSTS` environment variable to customize the platforms you are running tests on. To use a different vmpooler instance, use `--global-config pooling_api=http://customvmpooler/` when you use `beaker-hostgenerator` to generate `hosts.yaml`. Make sure you set `HOSTS` to the hosts file you just generated so the automation can find that file. See [The Hosts File](#the-hosts-file) for more detail on the hosts file. #### Docker To test with [the docker hypervisor](https://github.com/puppetlabs/beaker-docker), you will want to generate a custom hosts file. You will also mostly likely need to manually edit the file. See [The Hosts File](#the-hosts-file) for more detail on the hosts file. To create a hosts file with a centos 7 master and a centos 7 agent, we can use the following beaker-hostgenerator command `bundle exec beaker-hostgenerator centos7-64m-centos7-64a --disable-default-role --osinfo-version 1 --hypervisor docker > hosts.yaml` Which will produce a file called `hosts.yaml` that contains the following: ``` --- HOSTS: centos7-64-1: docker_cmd: - "/sbin/init" image: centos:7 platform: centos-7-x86_64 packaging_platform: el-7-x86_64 docker_image_commands: - cp /bin/true /sbin/agetty - yum install -y crontabs initscripts iproute openssl sysvinit-tools tar wget which ss hypervisor: docker roles: - master centos7-64-2: docker_cmd: - "/sbin/init" image: centos:7 platform: centos-7-x86_64 packaging_platform: el-7-x86_64 docker_image_commands: - cp /bin/true /sbin/agetty - yum install -y crontabs initscripts iproute openssl sysvinit-tools tar wget which ss hypervisor: docker roles: - agent CONFIG: nfs_server: none consoleport: 443 ``` Run acceptance tests against pre-built puppet-agent packages with `bundle exec rake ci:test:aio SHA={sha|tag} TESTS=path/to/test.rb HOSTS=hosts.yaml` Note that if you are not running tests against the master branch and you are installing the latest puppetserver package, you will likely need to set `RELEASE_STREAM` to pick up the correct server version. Please see the section on [environment variables](#environment-variables) for more information. When you generate your [hosts file](#the-hosts-file), [beaker-hostgenerator][] does its best to populate the values as logically as possible. You will likely want to update or modify them to suite your needs. With `image`, [beaker-hostgenerator][] does its best to guess the most logical image string based on the platform you are building. For the most part, this should work without interference, but if you are using a custom docker image or do not want the default, then you will have to manually update this string. Not every string beaker-hostgenerator uses to populate this variable will be valid. `docker_image_commands` is automatically populated when generating the hosts file with [beaker-hostgenerator][]. This has already been set for a handful of host types, but may not be set for all. * TODO I only tried this once using a docker image that already had puppetserver installed as the master host. The image I used took forever to provision, so I gave up. If we want to continue down this route, we need to make sure the setup steps can check if puppetserver has already been installed so that we don't try to install it agian. * TODO There's something odd with `docker_mage_entrypoint` versus `docker_cmd`. We should clarify the difference between these two values. I don't quite understand what the difference is between them. * TODO These docker containers have to run in privileged mode (or systemd, among possibly other things, won't function as we need them to). This is not ideal if you're testing code that affects your OS (ie running docker on linux without a docker machine in between the container and your laptop). BE CAREFUL #### Static Hosts This is not recommended unless you are familiar with how beaker and beaker-puppet provision hosts. To test on a server that's already been spun up or doesn't require a hypervisor, you should set the name of the host to the FQDN of the server you want to use, then remove the hypervisor and template settings. This is not recommended, and you may run into issues with failures or overwritten configuration due to either beaker provision steps or test provisioning steps. ``` --- HOSTS: azeqdqmk14mvu3g.delivery.puppetlabs.net: platform: el-7-x86_64 packaging_platform: el-7-x86_64 roles: - master ``` ------------- ## Running Tests ### Testing with pre-built packages ``` bundle exec rake ci:test: SHA={sha|tag} ``` This is the primary method that we use to run puppet acceptance tests. It requires puppet-agent packages that have been built with the version of the puppet code that you want to test. As building packages usually takes quite a bit of time, this method requires some patience. You are required to set `SHA` when running acceptance tests against pre-built packages. #### Testing a specific version If you are testing a specific version, `SHA` must be set to a value that exists on the path `#{ENV['DEV_BUILDS_URL']}/puppet-agent/#{ENV['SHA']}`. Note that this value corresponds to the puppet-agent package, not to puppet. `DEV_BUILDS_URL` defaults to the internal build server that is only accessible to Puppet, Inc. employees. The method called here depends on information written to a yaml file in that directory. Though you can override DEV_BUILDS_URL, the automation here is very specific and likely will not work as you are expecting it to. ``` bundle exec rake ci:test:aio SHA=3cfbac6857c10efc5b1e02262cfd7b849bb9c4b2 ``` ``` bundle exec rake ci:test:aio SHA=6.0.5 ``` #### Testing Nightlies If you do not have access to internal infrastructure, you can test against packages that have been made available on nightlies.puppet.com. Currently, you cannot specify a specific version. Instead, you have to use the latest shipped package for the release stream you are interested in. To do this, `SHA` must be set to `latest`. If you want to modify the release stream you are testing, `RELEASE_STREAM` can be modified. It defaults to `puppet` which should correspond to the latest stream available. If you want to modify `RELEASE_STREAM`, set it to an available repo, such as `puppet5`. ``` bundle exec rake ci:test:aio SHA=latest RELEASE_STREAM=puppet5 ``` ### Testing with Git ``` bundle exec rake ci:test:git SHA={sha|tag|branch} ``` #### From a repo on a git server Though we primarily run acceptance tests against a built package, it is possible to run these tests with a git checkout. This is most useful when testing locally to speed up the feedback cycle. When testing from a github repo we need to unpack the appropriate [runtime archive](https://github.com/puppetlabs/puppet-runtime) for the platform we are testing on. These pre-built archives are stored on an internal server, and are currently only available to Puppet, Inc. employees. With these archives, we get all of the runtime dependencies that are usually provided as a part of the puppet agent package. This allows us to replicate the runtime environment produced via a package install for the purpose of running acceptance tests. When testing with git, `SHA` can be set to any git artifact: a long sha, a short sha, a tag, a branch name, etc. What happens is that we write a gemfile with the details of the puppet repo, pointing to the artifact referenced with `SHA`. Then when we run `bundle install` on the testing host, bundler grabs puppet from wherever the gemfile points. If the git artifact referenced is not from the puppetlabs repo, you can use `FORK` to point to a different github namespace. Likewise, if the artifact you want to access is not available on `github.com` but a custom git server, you can set `SERVER` to customize the git uri bundler pulls from. For more details on these environment variables, run `bundle exec rake ci:help`. As an example, if I have a development branch (`developent/master/major-feature`) that I'm working on and it only exists in my fork of puppet (`github.com/joeschmoe/puppet`), then I will run ``` bundle exec rake ci:test:git SHA=developent/master/major-feature FORK=joeschmoe ``` Please note that any changes you want to test must be pushed up to your github server. This is how we access the code to be tested. #### From a local repo If you are testing with git and using the docker hypervisor, you can run tests against the puppet checkout on your local system. You need to update your hosts file to add `mount_folders` to the docker host where you want the checkout of puppet to be available. Here, `host_path` is the path to puppet on your local machine. The `container_path` is where puppet will end up on the docker image, so you can leave it as `/build/puppet`. Note that although `SHA` is required, it is never used in this workflow. For consistency, I would recommend setting `SHA` to your working branch name. We still need access to our runtime dependencies when testing against a local git checkout. When we are testing with the docker hypervisor, we assume that the docker image you are using will have this. As of this writing (Jan. 2019), the docker image you'll want to use for these tests is not public. The image is called `agent-runtime-{branch}`, where `{branch}` is the branch of puppet you are testing. This image includes everything we build as a part of [the runtime archive](https://github.com/puppetlabs/puppet-runtime). These components are normally provided as a part of the puppet agent package. ``` --- HOSTS: debian8-64-1: hypervisor: docker docker_image_entrypoint: "/sbin/init" image: pcr-internal.puppet.net/pe-and-platform/agent-runtime-master:201810110.17.gb5afc66 platform: debian-8-amd64 packaging_platform: debian-8-amd64 docker_image_commands: - rm -f /usr/sbin/policy-rc.d - systemctl mask getty@tty1.service getty-static.service - apt-get update && apt-get install -y cron locales-all net-tools wget mount_folders: puppet: host_path: ~/puppet container_path: /build/puppet roles: - agent ``` For more details on testing with docker, see [the docker section](#docker). Remember that `HOSTS` must be set to your hosts file for the automation to honor it. ### Testing with Gems Currently, running acceptance tests with gems is not working. ``` bundle exec rake ci:test:gem ``` ### Rerunning Failed Tests The rake tasks we use here take advantage of a newer feature in beaker that gives us quite a bit of flexibility. We take advantage of beaker subcommands. Subcommands are individual beaker invocations that are used to run the different stages of running tests: provisioning, pre-suite setup, tests, etc. We do this by writing state to the file `.beaker/subcommand_options.yaml`. With each new invocation of a subcommand, beaker will check for this file and load the contents if the file exists. The important thing about this feature is that you can rerun tests without going through the entire provisioning process every time. To ensure your hosts aren't cleaned up after a run, set `OPTIONS='--preserve-hosts=always'`. With this set, we can rerun a failed test using the infrastructure beaker has already provisioned. ``` bundle exec rake ci:test:aio OPTIONS='--preserve-hosts=always' SHA=6.0.5 ``` If this run fails because a small handful of tests fail, you can rerun only those tests that failed. For example, assume that `tests/resource/package/yum.rb` and `tests/node/check_woy_cache_works.rb` both had failing tests. you can run ``` bundle exec beaker exec tests/resource/package/yum.rb,tests/node/check_woy_cache_works.rb ``` This should work regardless of which hypervisor or testing method you are using. ------------- ## Writing Tests Read more about writing beaker tests in beaker. Check out the [tutorials section](https://github.com/puppetlabs/beaker/tree/master/docs/tutorials) and [how to write a quick test](https://github.com/puppetlabs/beaker/blob/master/docs/tutorials/lets_write_a_test.md) ------------- ## Getting Help ### On the web * [Puppet help messageboard](http://puppet.com/community/get-help) * [General GitHub documentation](http://help.github.com/) ### On chat * Slack (slack.puppet.com) #testing, #puppet-dev, #windows [bundler]: https://rubygems.org/gems/bundler [rspec-puppet]: http://rspec-puppet.com/ [rspec-puppet_docs]: http://rspec-puppet.com/documentation/ [beaker]: https://github.com/puppetlabs/beaker [beaker-puppet]: https://github.com/puppetlabs/beaker-puppet [beaker-hostgenerator]: https://github.com/puppetlabs/beaker-hostgenerator puppetlabs-puppet-789f600/acceptance/Rakefile000066400000000000000000000110321470131746300212270ustar00rootroot00000000000000require 'beaker-puppet' Beaker::DSL::Helpers::RakeHelpers.load_tasks namespace :ci do namespace :test do desc <<-EOS Run a limited but representative subset of acceptance tests against puppet-agent (AIO) packages. This task is intended to reduce testing time on a per-commit basis. $ SHA= bundle exec rake ci:test:quick SHA should be the full SHA for the puppet-agent package. EOS task :quick => ['ci:check_env', 'ci:gen_hosts'] do ENV['TESTS'] = get_test_sample.join(",") Rake::Task["ci:test:aio"].invoke end desc <<-EOS Run tests on docker quickly and easily. The docker container is set up to mount your puppet directory in the container. This means you can edit code or test files and rerun tests without reconfiguring your test environment. Defaults to running all tests unless TESTS is set. TESTS is a comma seperated list of test files to run. $ bundle exec rake ci:test:docker TESTS='path/to/test.rb,path/to/another/test.rb' By default, tests are run on a centos 7 host. To change the host, set HOSTS to a valid host string according to beaker-hostgenerator requirements. All tests marked with a server tag will be skipped. This task skips all cleanup. Please be sure to run `bundle exec beaker destroy` to clean up docker containers used for testing. EOS task :docker do begin ENV['HOSTS'] ||= 'centos7-64a' ENV['SHA'] ||= `git rev-parse HEAD`.chomp ENV['OPTIONS'] ||= '--preserve-hosts=always' ENV['OPTIONS'] += ' --test-tag-exclude=server' Rake::Task["ci:gen_hosts"].invoke('docker') hosts_file_content = YAML.load_file ENV['HOSTS'] hosts_file_content['HOSTS'].each do |host| host[1]['mount_folders'] = { 'puppet' => { 'host_path' => "#{File.dirname(__dir__)}" , 'container_path' => '/build/puppet' } } host[1]['tag'] = 'acceptance_test_host' end File.open(ENV['HOSTS'], "w") { |f| f.write(YAML.dump(hosts_file_content)) } Rake::Task["ci:test:git"].invoke ensure puts <<-EOF ************************ You can modify puppet code or tests and rerun tests without modifying your test environment. To rerun a test or set of tests, pass a comma seperated list of tests to: $ bundle exec beaker exec path/to/test.rb or $ bundle exec beaker exec path/to/test.rb,path/to/another/test.rb ************************ This task skips all clean up so you can rerun tests. Don't forget to clean up after yourself! To clean up the docker containers used to run tests, run: $ bundle exec beaker destroy ************************ EOF end end end namespace :sync do task :windows do raise 'WIN_MACHINE environment variable is required' unless ENV['WIN_MACHINE'] win_machine = ENV['WIN_MACHINE'] + '.delivery.puppetlabs.net' path = ENV['LIB_DIR'] || 'type' # 'lib/puppet' prefix is implicit. dest_path = path.split('/')[0...-1].join system("scp -r #{File.dirname(__FILE__)}/../lib/puppet/#{path} Administrator@#{win_machine}:'C:/Program\\ Files/Puppet\\ Labs/Puppet/puppet/lib/ruby/vendor_ruby/puppet/#{dest_path}'") end end end def get_test_sample # This set represents a reasonable sample of puppet acceptance tests, # covering a wide range of features and code susceptible to regressions. tests = [ 'tests/direct_puppet/cached_catalog_remediate_local_drift.rb', 'tests/resource/file/content_attribute.rb', 'tests/face/loadable_from_modules.rb', 'tests/language/functions_in_puppet_language.rb', 'tests/parser_functions/calling_all_functions.rb', 'tests/ticket_4622_filebucket_diff_test.rb', 'tests/pluginsync/4420_pluginfacts_should_be_resolvable_on_agent.rb', 'tests/ssl/puppet_cert_generate_and_autosign.rb', 'tests/resource/package/yum.rb', 'tests/resource/service/ticket_5024_systemd_enabling_masked_service.rb', 'tests/resource/service/puppet_service_management.rb' ] # Add any tests modified within the last two weeks to the list, excluding # deleted ones. We can't rely on --diff-filter, because an acceptance # test may be modified and then deleted in the same time range. modified = `git log --name-only --pretty="format:" --since 2.weeks ./tests` tests += modified.split("\n").reject do |s| s.empty? end.collect do |s| s.sub('acceptance/', '') end.select do |s| s =~ /\.rb$/ end.find_all do |s| File.exist?(s) end tests.uniq.sort end puppetlabs-puppet-789f600/acceptance/config/000077500000000000000000000000001470131746300210325ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/config/aio/000077500000000000000000000000001470131746300216025ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/config/aio/options.rb000066400000000000000000000007001470131746300236170ustar00rootroot00000000000000{ :type => 'aio', 'is_puppetserver' => true, 'use-service' => true, # use service scripts to start/stop stuff 'puppetservice' => 'puppetserver', 'puppetserver-confdir' => '/etc/puppetlabs/puppetserver/conf.d', 'puppetserver-config' => '/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf', :post_suite => [ 'teardown/common/099_Archive_Logs.rb', ], } puppetlabs-puppet-789f600/acceptance/config/gem/000077500000000000000000000000001470131746300216025ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/config/gem/options.rb000066400000000000000000000001141470131746300236160ustar00rootroot00000000000000{ # Use `git` so that we have a sane ruby environment :type => 'git', } puppetlabs-puppet-789f600/acceptance/config/git/000077500000000000000000000000001470131746300216155ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/config/git/options.rb000066400000000000000000000006641470131746300236430ustar00rootroot00000000000000{ :type => 'git', :install => [ 'puppet', ], 'is_puppetserver' => false, 'use-service' => true, # use service scripts to start/stop stuff 'puppetservice' => 'puppetserver', 'puppetserver-confdir' => '/etc/puppetlabs/puppetserver/conf.d', 'puppetserver-config' => '/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf' } puppetlabs-puppet-789f600/acceptance/config/nodes/000077500000000000000000000000001470131746300221425ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/config/nodes/aix-53-power.yaml000066400000000000000000000005401470131746300251650ustar00rootroot00000000000000--- HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vmpooler template: redhat-7-x86_64 pe-aix-53-acceptance: roles: - agent platform: aix-5.3-power hypervisor: none vmhostname: pe-aix-53-acceptance.delivery.puppetlabs.net CONFIG: pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/aix-61-power.yaml000066400000000000000000000005371470131746300251720ustar00rootroot00000000000000--- HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vmpooler template: redhat-7-x86_64 pe-aix-61-acceptance: roles: - agent platform: aix-6.1-power hypervisor: none vmhostname: pe-aix-61-acceptance.delivery.puppetlabs.net CONFIG: pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/aix-71-power.yaml000066400000000000000000000005371470131746300251730ustar00rootroot00000000000000--- HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vmpooler template: redhat-7-x86_64 pe-aix-71-acceptance: roles: - agent platform: aix-7.1-power hypervisor: none vmhostname: pe-aix-71-acceptance.delivery.puppetlabs.net CONFIG: pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/gem.yaml000066400000000000000000000011521470131746300235750ustar00rootroot00000000000000--- HOSTS: win-2012r2-rubyx86: roles: - agent platform: windows-2012r2-64 ruby_arch: x86 hypervisor: vmpooler template: win-2012r2-x86_64 win-2012r2-rubyx64: roles: - agent platform: windows-2012r2-64 ruby_arch: x64 hypervisor: vmpooler template: win-2012r2-x86_64 osx-1010: roles: - agent platform: osx-10.10-x86_64 hypervisor: vmpooler template: osx-1010-x86_64 redhat-7: roles: - agent platform: el-7-x86_64 hypervisor: vmpooler template: redhat-7-x86_64 CONFIG: pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/huaweios-6-powerpc.yaml000066400000000000000000000005041470131746300264710ustar00rootroot00000000000000--- HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vmpooler template: redhat-7-x86_64 huawei-ce6850-2-debian-vm-eth0.ops.puppetlabs.net: roles: - agent platform: huaweios-6-powerpc hypervisor: none CONFIG: pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/pe/000077500000000000000000000000001470131746300225465ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-5-32ma-32da-32da000066400000000000000000000011551470131746300261460ustar00rootroot00000000000000HOSTS: centos-5-i386-master: roles: - master - agent platform: el-5-i386 template: centos-5-i386 hypervisor: vcloud centos-5-i386-dashboard: roles: - dashboard - agent platform: el-5-i386 template: centos-5-i386 hypervisor: vcloud centos-5-i386-database: roles: - database - agent platform: el-5-i386 template: centos-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-5-32mda000066400000000000000000000007441470131746300250370ustar00rootroot00000000000000HOSTS: centos-5-i386: roles: - master - dashboard - database - agent platform: el-5-i386 template: centos-5-i386 hypervisor: vcloud centos-5-i386-agent: roles: - agent platform: el-5-i386 template: centos-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-5-64ma-64da-64da000066400000000000000000000011771470131746300261710ustar00rootroot00000000000000HOSTS: centos-5-x86_64-master: roles: - master - agent platform: el-5-x86_64 template: centos-5-x86_64 hypervisor: vcloud centos-5-x86_64-dashboard: roles: - dashboard - agent platform: el-5-x86_64 template: centos-5-x86_64 hypervisor: vcloud centos-5-x86_64-database: roles: - database - agent platform: el-5-x86_64 template: centos-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-5-64mda000066400000000000000000000007601470131746300250420ustar00rootroot00000000000000HOSTS: centos-5-x86_64: roles: - master - dashboard - database - agent platform: el-5-x86_64 template: centos-5-x86_64 hypervisor: vcloud centos-5-x86_64-agent: roles: - agent platform: el-5-x86_64 template: centos-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-6-32ma-32da-32da000066400000000000000000000011551470131746300261470ustar00rootroot00000000000000HOSTS: centos-6-i386-master: roles: - master - agent platform: el-6-i386 template: centos-6-i386 hypervisor: vcloud centos-6-i386-dashboard: roles: - dashboard - agent platform: el-6-i386 template: centos-6-i386 hypervisor: vcloud centos-6-i386-database: roles: - database - agent platform: el-6-i386 template: centos-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-6-32mda000066400000000000000000000007441470131746300250400ustar00rootroot00000000000000HOSTS: centos-6-i386: roles: - master - dashboard - database - agent platform: el-6-i386 template: centos-6-i386 hypervisor: vcloud centos-6-i386-agent: roles: - agent platform: el-6-i386 template: centos-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-6-64ma-64da-64da000066400000000000000000000011771470131746300261720ustar00rootroot00000000000000HOSTS: centos-6-x86_64-master: roles: - master - agent platform: el-6-x86_64 template: centos-6-x86_64 hypervisor: vcloud centos-6-x86_64-dashboard: roles: - dashboard - agent platform: el-6-x86_64 template: centos-6-x86_64 hypervisor: vcloud centos-6-x86_64-database: roles: - database - agent platform: el-6-x86_64 template: centos-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-6-64mda000066400000000000000000000007601470131746300250430ustar00rootroot00000000000000HOSTS: centos-6-x86_64: roles: - master - dashboard - database - agent platform: el-6-x86_64 template: centos-6-x86_64 hypervisor: vcloud centos-6-x86_64-agent: roles: - agent platform: el-6-x86_64 template: centos-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/centos-6-64mda-sol-10-64a000066400000000000000000000007701470131746300264450ustar00rootroot00000000000000HOSTS: centos-6-x86_64: roles: - master - dashboard - database - agent platform: el-6-x86_64 template: centos-6-x86_64 hypervisor: vcloud solaris-10-x86_64-agent: roles: - agent platform: solaris-10-i386 template: solaris-10-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-6-32ma-32da-32da000066400000000000000000000011711470131746300260740ustar00rootroot00000000000000HOSTS: debian-6-i386-master: roles: - master - agent platform: debian-6-i386 template: debian-6-i386 hypervisor: vcloud debian-6-i386-dashboard: roles: - dashboard - agent platform: debian-6-i386 template: debian-6-i386 hypervisor: vcloud debian-6-i386-database: roles: - database - agent platform: debian-6-i386 template: debian-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-6-32mda000066400000000000000000000007541470131746300247700ustar00rootroot00000000000000HOSTS: debian-6-i386: roles: - master - dashboard - database - agent platform: debian-6-i386 template: debian-6-i386 hypervisor: vcloud debian-6-i386-agent: roles: - agent platform: debian-6-i386 template: debian-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-6-64ma-64da-64da000066400000000000000000000012051470131746300261110ustar00rootroot00000000000000HOSTS: debian-6-amd64-master: roles: - master - agent platform: debian-6-amd64 template: debian-6-x86_64 hypervisor: vcloud debian-6-amd64-dashboard: roles: - dashboard - agent platform: debian-6-amd64 template: debian-6-x86_64 hypervisor: vcloud debian-6-amd64-database: roles: - database - agent platform: debian-6-amd64 template: debian-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-6-64mda000066400000000000000000000007641470131746300247760ustar00rootroot00000000000000HOSTS: debian-6-amd64: roles: - master - dashboard - database - agent platform: debian-6-amd64 template: debian-6-x86_64 hypervisor: vcloud debian-6-amd64-agent: roles: - agent platform: debian-6-amd64 template: debian-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-7-32ma-32da-32da000066400000000000000000000011711470131746300260750ustar00rootroot00000000000000HOSTS: debian-7-i386-master: roles: - master - agent platform: debian-7-i386 template: debian-7-i386 hypervisor: vcloud debian-7-i386-dashboard: roles: - dashboard - agent platform: debian-7-i386 template: debian-7-i386 hypervisor: vcloud debian-7-i386-database: roles: - database - agent platform: debian-7-i386 template: debian-7-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-7-32mda000066400000000000000000000007541470131746300247710ustar00rootroot00000000000000HOSTS: debian-7-i386: roles: - master - dashboard - database - agent platform: debian-7-i386 template: debian-7-i386 hypervisor: vcloud debian-7-i386-agent: roles: - agent platform: debian-7-i386 template: debian-7-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-7-64ma-64da-64da000066400000000000000000000012051470131746300261120ustar00rootroot00000000000000HOSTS: debian-7-amd64-master: roles: - master - agent platform: debian-7-amd64 template: debian-7-x86_64 hypervisor: vcloud debian-7-amd64-dashboard: roles: - dashboard - agent platform: debian-7-amd64 template: debian-7-x86_64 hypervisor: vcloud debian-7-amd64-database: roles: - database - agent platform: debian-7-amd64 template: debian-7-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-7-64mda000066400000000000000000000007641470131746300247770ustar00rootroot00000000000000HOSTS: debian-7-amd64: roles: - master - dashboard - database - agent platform: debian-7-amd64 template: debian-7-x86_64 hypervisor: vcloud debian-7-amd64-agent: roles: - agent platform: debian-7-amd64 template: debian-7-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/debian-7-i386000066400000000000000000000005551470131746300245530ustar00rootroot00000000000000HOSTS: debian-7-i386: roles: - master - dashboard - database - agent platform: debian-7-i386 template: debian-7-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-5-32ma-32da-32da000066400000000000000000000011551470131746300261200ustar00rootroot00000000000000HOSTS: oracle-5-i386-master: roles: - master - agent platform: el-5-i386 template: oracle-5-i386 hypervisor: vcloud oracle-5-i386-dashboard: roles: - dashboard - agent platform: el-5-i386 template: oracle-5-i386 hypervisor: vcloud oracle-5-i386-database: roles: - database - agent platform: el-5-i386 template: oracle-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-5-32mda000066400000000000000000000007441470131746300250110ustar00rootroot00000000000000HOSTS: oracle-5-i386: roles: - master - dashboard - database - agent platform: el-5-i386 template: oracle-5-i386 hypervisor: vcloud oracle-5-i386-agent: roles: - agent platform: el-5-i386 template: oracle-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-5-64ma-64da-64da000066400000000000000000000011771470131746300261430ustar00rootroot00000000000000HOSTS: oracle-5-x86_64-master: roles: - master - agent platform: el-5-x86_64 template: oracle-5-x86_64 hypervisor: vcloud oracle-5-x86_64-dashboard: roles: - dashboard - agent platform: el-5-x86_64 template: oracle-5-x86_64 hypervisor: vcloud oracle-5-x86_64-database: roles: - database - agent platform: el-5-x86_64 template: oracle-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-5-64mda000066400000000000000000000007601470131746300250140ustar00rootroot00000000000000HOSTS: oracle-5-x86_64: roles: - master - dashboard - database - agent platform: el-5-x86_64 template: oracle-5-x86_64 hypervisor: vcloud oracle-5-x86_64-agent: roles: - agent platform: el-5-x86_64 template: oracle-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-6-32ma-32da-32da000066400000000000000000000011551470131746300261210ustar00rootroot00000000000000HOSTS: oracle-6-i386-master: roles: - master - agent platform: el-6-i386 template: oracle-6-i386 hypervisor: vcloud oracle-6-i386-dashboard: roles: - dashboard - agent platform: el-6-i386 template: oracle-6-i386 hypervisor: vcloud oracle-6-i386-database: roles: - database - agent platform: el-6-i386 template: oracle-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-6-32mda000066400000000000000000000007441470131746300250120ustar00rootroot00000000000000HOSTS: oracle-6-i386: roles: - master - dashboard - database - agent platform: el-6-i386 template: oracle-6-i386 hypervisor: vcloud oracle-6-i386-agent: roles: - agent platform: el-6-i386 template: oracle-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-6-64ma-64da-64da000066400000000000000000000011771470131746300261440ustar00rootroot00000000000000HOSTS: oracle-6-x86_64-master: roles: - master - agent platform: el-6-x86_64 template: oracle-6-x86_64 hypervisor: vcloud oracle-6-x86_64-dashboard: roles: - dashboard - agent platform: el-6-x86_64 template: oracle-6-x86_64 hypervisor: vcloud oracle-6-x86_64-database: roles: - database - agent platform: el-6-x86_64 template: oracle-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/oracle-6-64mda000066400000000000000000000007601470131746300250150ustar00rootroot00000000000000HOSTS: oracle-6-x86_64: roles: - master - dashboard - database - agent platform: el-6-x86_64 template: oracle-6-x86_64 hypervisor: vcloud oracle-6-x86_64-agent: roles: - agent platform: el-6-x86_64 template: oracle-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-5-32ma-32da-32da000066400000000000000000000011551470131746300261220ustar00rootroot00000000000000HOSTS: redhat-5-i386-master: roles: - master - agent platform: el-5-i386 template: redhat-5-i386 hypervisor: vcloud redhat-5-i386-dashboard: roles: - dashboard - agent platform: el-5-i386 template: redhat-5-i386 hypervisor: vcloud redhat-5-i386-database: roles: - database - agent platform: el-5-i386 template: redhat-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-5-32mda000066400000000000000000000007441470131746300250130ustar00rootroot00000000000000HOSTS: redhat-5-i386: roles: - master - dashboard - database - agent platform: el-5-i386 template: redhat-5-i386 hypervisor: vcloud redhat-5-i386-agent: roles: - agent platform: el-5-i386 template: redhat-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-5-64ma-64da-64da000066400000000000000000000011771470131746300261450ustar00rootroot00000000000000HOSTS: redhat-5-x86_64-master: roles: - master - agent platform: el-5-x86_64 template: redhat-5-x86_64 hypervisor: vcloud redhat-5-x86_64-dashboard: roles: - dashboard - agent platform: el-5-x86_64 template: redhat-5-x86_64 hypervisor: vcloud redhat-5-x86_64-database: roles: - database - agent platform: el-5-x86_64 template: redhat-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-5-64mda000066400000000000000000000007601470131746300250160ustar00rootroot00000000000000HOSTS: redhat-5-x86_64: roles: - master - dashboard - database - agent platform: el-5-x86_64 template: redhat-5-x86_64 hypervisor: vcloud redhat-5-x86_64-agent: roles: - agent platform: el-5-x86_64 template: redhat-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-6-32ma-32da-32da000066400000000000000000000011551470131746300261230ustar00rootroot00000000000000HOSTS: redhat-6-i386-master: roles: - master - agent platform: el-6-i386 template: redhat-6-i386 hypervisor: vcloud redhat-6-i386-dashboard: roles: - dashboard - agent platform: el-6-i386 template: redhat-6-i386 hypervisor: vcloud redhat-6-i386-database: roles: - database - agent platform: el-6-i386 template: redhat-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-6-32mda000066400000000000000000000007441470131746300250140ustar00rootroot00000000000000HOSTS: redhat-6-i386: roles: - master - dashboard - database - agent platform: el-6-i386 template: redhat-6-i386 hypervisor: vcloud redhat-6-i386-agent: roles: - agent platform: el-6-i386 template: redhat-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-6-64ma-64da-64da000066400000000000000000000011771470131746300261460ustar00rootroot00000000000000HOSTS: redhat-6-x86_64-master: roles: - master - agent platform: el-6-x86_64 template: redhat-6-x86_64 hypervisor: vcloud redhat-6-x86_64-dashboard: roles: - dashboard - agent platform: el-6-x86_64 template: redhat-6-x86_64 hypervisor: vcloud redhat-6-x86_64-database: roles: - database - agent platform: el-6-x86_64 template: redhat-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/redhat-6-64mda000066400000000000000000000007601470131746300250170ustar00rootroot00000000000000HOSTS: redhat-6-x86_64: roles: - master - dashboard - database - agent platform: el-6-x86_64 template: redhat-6-x86_64 hypervisor: vcloud redhat-6-x86_64-agent: roles: - agent platform: el-6-x86_64 template: redhat-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-5-32ma-32da-32da000066400000000000000000000012051470131746300267670ustar00rootroot00000000000000HOSTS: scientific-5-i386-master: roles: - master - agent platform: el-5-i386 template: scientific-5-i386 hypervisor: vcloud scientific-5-i386-dashboard: roles: - dashboard - agent platform: el-5-i386 template: scientific-5-i386 hypervisor: vcloud scientific-5-i386-database: roles: - database - agent platform: el-5-i386 template: scientific-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-5-32mda000066400000000000000000000007641470131746300256660ustar00rootroot00000000000000HOSTS: scientific-5-i386: roles: - master - dashboard - database - agent platform: el-5-i386 template: scientific-5-i386 hypervisor: vcloud scientific-5-i386-agent: roles: - agent platform: el-5-i386 template: scientific-5-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-5-64ma-64da-64da000066400000000000000000000012271470131746300270120ustar00rootroot00000000000000HOSTS: scientific-5-x86_64-master: roles: - master - agent platform: el-5-x86_64 template: scientific-5-x86_64 hypervisor: vcloud scientific-5-x86_64-dashboard: roles: - dashboard - agent platform: el-5-x86_64 template: scientific-5-x86_64 hypervisor: vcloud scientific-5-x86_64-database: roles: - database - agent platform: el-5-x86_64 template: scientific-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-5-64mda000066400000000000000000000010001470131746300256530ustar00rootroot00000000000000HOSTS: scientific-5-x86_64: roles: - master - dashboard - database - agent platform: el-5-x86_64 template: scientific-5-x86_64 hypervisor: vcloud scientific-5-x86_64-agent: roles: - agent platform: el-5-x86_64 template: scientific-5-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-6-32ma-32da-32da000066400000000000000000000012051470131746300267700ustar00rootroot00000000000000HOSTS: scientific-6-i386-master: roles: - master - agent platform: el-6-i386 template: scientific-6-i386 hypervisor: vcloud scientific-6-i386-dashboard: roles: - dashboard - agent platform: el-6-i386 template: scientific-6-i386 hypervisor: vcloud scientific-6-i386-database: roles: - database - agent platform: el-6-i386 template: scientific-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-6-32mda000066400000000000000000000007641470131746300256670ustar00rootroot00000000000000HOSTS: scientific-6-i386: roles: - master - dashboard - database - agent platform: el-6-i386 template: scientific-6-i386 hypervisor: vcloud scientific-6-i386-agent: roles: - agent platform: el-6-i386 template: scientific-6-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-6-64ma-64da-64da000066400000000000000000000012271470131746300270130ustar00rootroot00000000000000HOSTS: scientific-6-x86_64-master: roles: - master - agent platform: el-6-x86_64 template: scientific-6-x86_64 hypervisor: vcloud scientific-6-x86_64-dashboard: roles: - dashboard - agent platform: el-6-x86_64 template: scientific-6-x86_64 hypervisor: vcloud scientific-6-x86_64-database: roles: - database - agent platform: el-6-x86_64 template: scientific-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/scientific-6-64mda000066400000000000000000000010001470131746300256540ustar00rootroot00000000000000HOSTS: scientific-6-x86_64: roles: - master - dashboard - database - agent platform: el-6-x86_64 template: scientific-6-x86_64 hypervisor: vcloud scientific-6-x86_64-agent: roles: - agent platform: el-6-x86_64 template: scientific-6-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/sles-11-32ma-32da-32da000066400000000000000000000011601470131746300256720ustar00rootroot00000000000000HOSTS: sles-11-i386-master: roles: - master - agent platform: sles-11-i386 template: sles-11-i386 hypervisor: vcloud sles-11-i386-dashboard: roles: - dashboard - agent platform: sles-11-i386 template: sles-11-i386 hypervisor: vcloud sles-11-i386-database: roles: - database - agent platform: sles-11-i386 template: sles-11-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/sles-11-32mda000066400000000000000000000007461470131746300245710ustar00rootroot00000000000000HOSTS: sles-11-i386: roles: - master - dashboard - database - agent platform: sles-11-i386 template: sles-11-i386 hypervisor: vcloud sles-11-i386-agent: roles: - agent platform: sles-11-i386 template: sles-11-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/sles-11-64ma-64da-64da000066400000000000000000000012021470131746300257060ustar00rootroot00000000000000HOSTS: sles-11-x86_64-master: roles: - master - agent platform: sles-11-x86_64 template: sles-11-x86_64 hypervisor: vcloud sles-11-x86_64-dashboard: roles: - dashboard - agent platform: sles-11-x86_64 template: sles-11-x86_64 hypervisor: vcloud sles-11-x86_64-database: roles: - database - agent platform: sles-11-x86_64 template: sles-11-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/sles-11-64mda000066400000000000000000000007621470131746300245740ustar00rootroot00000000000000HOSTS: sles-11-x86_64: roles: - master - dashboard - database - agent platform: sles-11-x86_64 template: sles-11-x86_64 hypervisor: vcloud sles-11-x86_64-agent: roles: - agent platform: sles-11-x86_64 template: sles-11-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/solaris-10-64a000066400000000000000000000006641470131746300247610ustar00rootroot00000000000000HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vcloud template: redhat-7-x86_64 agent: roles: - agent platform: solaris-10-x86_64 hypervisor: vcloud template: solaris-10-x86_64 CONFIG: datastore: instance0 resourcepool: delivery/Quality Assurance/FOSS/Dynamic folder: Delivery/Quality Assurance/FOSS/Dynamic pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/pe/solaris-11-64a000066400000000000000000000006641470131746300247620ustar00rootroot00000000000000HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vcloud template: redhat-7-x86_64 agent: roles: - agent platform: solaris-11-x86_64 hypervisor: vcloud template: solaris-11-x86_64 CONFIG: datastore: instance0 resourcepool: delivery/Quality Assurance/FOSS/Dynamic folder: Delivery/Quality Assurance/FOSS/Dynamic pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1004-32ma-32da-32da000066400000000000000000000012271470131746300264150ustar00rootroot00000000000000HOSTS: ubuntu-1004-i386-master: roles: - master - agent platform: ubuntu-10.04-i386 template: ubuntu-1004-i386 hypervisor: vcloud ubuntu-1004-i386-dashboard: roles: - dashboard - agent platform: ubuntu-10.04-i386 template: ubuntu-1004-i386 hypervisor: vcloud ubuntu-1004-i386-database: roles: - database - agent platform: ubuntu-10.04-i386 template: ubuntu-1004-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1004-32mda000066400000000000000000000007731470131746300253100ustar00rootroot00000000000000HOSTS: ubuntu-1004-i386: roles: - master - dashboard - database - agent platform: ubuntu-10.04-i386 template: ubuntu-1004-i386 hypervisor: vcloud ubuntu-1004-agent: roles: - agent platform: ubuntu-10.04-i386 template: ubuntu-1004-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1004-64ma-64da-64da000066400000000000000000000012431470131746300264320ustar00rootroot00000000000000HOSTS: ubuntu-1004-amd64-master: roles: - master - agent platform: ubuntu-10.04-amd64 template: ubuntu-1004-x86_64 hypervisor: vcloud ubuntu-1004-amd64-dashboard: roles: - dashboard - agent platform: ubuntu-10.04-amd64 template: ubuntu-1004-x86_64 hypervisor: vcloud ubuntu-1004-amd64-database: roles: - database - agent platform: ubuntu-10.04-amd64 template: ubuntu-1004-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1004-64mda000066400000000000000000000010021470131746300252770ustar00rootroot00000000000000HOSTS: ubuntu-1004-amd64: roles: - master - dashboard - database - agent platform: ubuntu-10.04-amd64 template: ubuntu-1004-x86_64 hypervisor: vcloud ubuntu-1004-agent: roles: - agent platform: ubuntu-10.04-amd64 template: ubuntu-1004-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1204-32ma-32da-32da000066400000000000000000000012271470131746300264170ustar00rootroot00000000000000HOSTS: ubuntu-1204-i386-master: roles: - master - agent platform: ubuntu-12.04-i386 template: ubuntu-1204-i386 hypervisor: vcloud ubuntu-1204-i386-dashboard: roles: - dashboard - agent platform: ubuntu-12.04-i386 template: ubuntu-1204-i386 hypervisor: vcloud ubuntu-1204-i386-database: roles: - database - agent platform: ubuntu-12.04-i386 template: ubuntu-1204-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1204-32mda000066400000000000000000000007731470131746300253120ustar00rootroot00000000000000HOSTS: ubuntu-1204-i386: roles: - master - dashboard - database - agent platform: ubuntu-12.04-i386 template: ubuntu-1204-i386 hypervisor: vcloud ubuntu-1204-agent: roles: - agent platform: ubuntu-12.04-i386 template: ubuntu-1204-i386 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1204-64ma-64da-64da000066400000000000000000000012431470131746300264340ustar00rootroot00000000000000HOSTS: ubuntu-1204-amd64-master: roles: - master - agent platform: ubuntu-12.04-amd64 template: ubuntu-1204-x86_64 hypervisor: vcloud ubuntu-1204-amd64-dashboard: roles: - dashboard - agent platform: ubuntu-12.04-amd64 template: ubuntu-1204-x86_64 hypervisor: vcloud ubuntu-1204-amd64-database: roles: - database - agent platform: ubuntu-12.04-amd64 template: ubuntu-1204-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/pe/ubuntu-1204-64mda000066400000000000000000000010021470131746300253010ustar00rootroot00000000000000HOSTS: ubuntu-1204-amd64: roles: - master - dashboard - database - agent platform: ubuntu-12.04-amd64 template: ubuntu-1204-x86_64 hypervisor: vcloud ubuntu-1204-agent: roles: - agent platform: ubuntu-12.04-amd64 template: ubuntu-1204-x86_64 hypervisor: vcloud CONFIG: nfs_server: none consoleport: 443 datastore: instance0 folder: Delivery/Quality Assurance/Enterprise/Dynamic resourcepool: delivery/Quality Assurance/Enterprise/Dynamic puppetlabs-puppet-789f600/acceptance/config/nodes/solaris-10-sparc.yaml000066400000000000000000000005471470131746300260340ustar00rootroot00000000000000--- HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vmpooler template: redhat-7-x86_64 solaris-10-sparc: roles: - agent platform: solaris-10-sparc hypervisor: none ip: 10.32.121.124 vmhostname: sol10-1.delivery.puppetlabs.net CONFIG: pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/config/nodes/solaris-11-sparc.yaml000066400000000000000000000005471470131746300260350ustar00rootroot00000000000000--- HOSTS: master: roles: - master platform: el-7-x86_64 hypervisor: vmpooler template: redhat-7-x86_64 solaris-11-sparc: roles: - agent platform: solaris-11-sparc hypervisor: none ip: 10.32.114.245 vmhostname: sol11-1.delivery.puppetlabs.net CONFIG: pooling_api: http://vmpooler.delivery.puppetlabs.net/ puppetlabs-puppet-789f600/acceptance/fixtures/000077500000000000000000000000001470131746300214365ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/MockInstaller.cs000066400000000000000000000025531470131746300245410ustar00rootroot00000000000000/* The MockInstaller is a C# class representing a stubbed exe installer. We will compile this class into an installable .exe file. A MockInstaller _MUST_ come alongside a MockUninstaller, so we can uninstall the fake package from the system */ using System; public class MockInstaller { public static void Main() { try { %{install_commands} } catch { Environment.Exit(1003); } string keyName = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; Microsoft.Win32.RegistryKey key; key = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(keyName + "\\%{package_display_name}"); /* Puppet deems an exe package 'installable' by identifying whether or not the following registry values exist in the Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\PackageName key: * DisplayName * DisplayVersion * UninstallString So we must set those values in the registry for this to be an 'installable package' manageable by puppet. */ key.SetValue("DisplayName", "%{package_display_name}"); key.SetValue("DisplayVersion", "1.0.0"); key.SetValue("UninstallString", @"%{uninstaller_location}"); key.Close(); Console.WriteLine("Installing..."); } } puppetlabs-puppet-789f600/acceptance/fixtures/MockService.cs000066400000000000000000000055601470131746300242050ustar00rootroot00000000000000/* The MockService is a C# class representing a stubbed service. We will compile this class into the service's .exe file. Here, we implement four methods: * OnStart -- called when SCM starts the service * OnPause -- called when SCM pauses the service * OnContinue -- called when SCM resumes a paused service * OnStop -- called when SCM stops a service Before calling one of these 'On' methods, the ServiceBase class sets the service state to the corresponding PENDING state. The service state is in this PENDING state until the 'On' method is finished, whereby it is then transitioned into the corresponding final state. Thus if we sleep for a few seconds in the 'On' method, then note that SCM will report our service state as being in the PENDING state while we're asleep. For example, if the 'On' method is 'OnStart', the service state is set to START_PENDING before calling 'OnStart', is START_PENDING while executing 'OnStart', and then is set to RUNNING after exiting 'OnStart'. When testing the Windows service provider, we really want to test to ensure that it handles the state transitions correctly. For example, we want to check that: * It waits for the appropriate PENDING state to finish * It sets the service state to the appropriate final state The reason we want to do this is because our service provider is communicating with SCM directly, which does not care how the service implements these transitions so long as it implements them. C#'s ServiceBase class implements these state transitions for us. Thus by going to sleep in all of our 'On' methods, we simulate transitioning to the corresponding PENDING state. When we wake-up and exit the 'On' method, we will transition to the appropriate final state. NOTE: Normally, you're supposed to have the service thread in a separate process. The 'On' methods in this class would send signals to the service thread and then wait for those signals to be processed. Sending and waiting for these signals is quite hard and unnecessary for our use-case, which is why our MockService does not have the service thread. */ using System; using System.ServiceProcess; public class MockService : ServiceBase { public static void Main() { System.ServiceProcess.ServiceBase.Run(new MockService()); } public MockService() { ServiceName = "%{service_name}"; CanStop = true; CanPauseAndContinue = true; } private void StubPendingTransition(int seconds) { RequestAdditionalTime(2000); System.Threading.Thread.Sleep(seconds * 1000); } protected override void OnStart(string [] args) { StubPendingTransition(%{start_sleep}); } protected override void OnPause() { StubPendingTransition(%{pause_sleep}); } protected override void OnContinue() { StubPendingTransition(%{continue_sleep}); } protected override void OnStop() { StubPendingTransition(%{stop_sleep}); } } puppetlabs-puppet-789f600/acceptance/fixtures/MockUninstaller.cs000066400000000000000000000015601470131746300251010ustar00rootroot00000000000000/* The MockUninstaller is a C# class representing a stubbed exe uninstaller. We will compile this class into an usable .exe file. A MockInstaller _MUST_ come alongside a MockUninstaller, so we can uninstall the fake package from the system */ using System; public class MockInstaller { public static void Main() { try { %{uninstall_commands} } catch { Environment.Exit(1003); } string keyName = "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; Console.WriteLine("Uninstalling..."); /* Remove the entire registry key created by the installer exe */ using (Microsoft.Win32.RegistryKey _key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(keyName, true)) { _key.DeleteSubKeyTree("%{package_display_name}"); } } } puppetlabs-puppet-789f600/acceptance/fixtures/debian-repo/000077500000000000000000000000001470131746300236235ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/debian-repo/Packages.gz000066400000000000000000000010251470131746300257010ustar00rootroot00000000000000p^͎0 {"/;B,^ViWNi,q\N[*_Aq6-#weӮW/ZðJYKˣqniحmҫwü Soe_U96x3YM_wQv~yTTgÒGیvnxU 9wb!p)3!nWX[ XgpZdE1By5H EkO^)CU 2P3Z2 `jȩKmaZ{n_ts|t0UH_|6PuZԭKh^7[ 3Q a ^ $AA*5Ao`]MA&>%:-+AlM)24]9c 9j0'+ .B`ڶ/+|¿*Hb<B. Z@@J(EQld9 ]4 debian-binary 1584434776 0 0 100644 4 ` 2.0 control.tar.gz 1584434776 0 0 100644 471 ` j@`)EJviHZz\KSk[iW̮Z|wdK mzqJ`>,dVLz\ey8χ^eEYp|^ #C4oDw|w׺_<:K _+hwO]n<.rxܚjC֓ 0M^RȫFLשwƺR@8xe WHsfcZZa~)%;["Ü7|@mT"/u~gSo tv|nLlLs8@WW{ ޛjw-:209|8 <#?Ssa debian-binary 1584434853 0 0 100644 4 ` 2.0 control.tar.gz 1584434853 0 0 100644 321 ` J0촓^x[]g2 &g7ma@AGKړt\e9q|yj'T&D6|*Io͎_ŗ< debian-binary 1584435079 0 0 100644 4 ` 2.0 control.tar.gz 1584435079 0 0 100644 314 ` J0:"mFPtD'p*n|v3- lFfhI')Βˢ1>y9/lyEBu>H&JعۺT:S ?_yYP[H$uKkm{ulWNso\RfG*Kk`؄Ҋ3Vk⪁FZq-M⭹ ȎJjc5r Nr'2&l͑~<ݮMOtOǴ7< غzj(data.tar.gz 1584435079 0 0 100644 219 ` Mj0@a} %מ+Y18 jjzFB)$,476HS mΊoEYBJ7jKfUCk{_Tm )lpt 4790bf12a69929c1a665ba4116753cac22d49200b44ca9654edd4cf9411817f509209d0a36a52e71510a03f98977d504d4f5161b >5?|d & j A[rx     $,@X(GHIX Y\ ](^Kbdeflt uv,06xCkernel-devel-puppet3.10.01062.1.1Development package for building kernel modules to match the kernelThis package provides kernel headers and makefiles sufficient to build modules against the kernel package.aBbetter-converse.delivery.puppetlabs.netCentOSGPLv2CentOS BuildSystem System Environment/Kernelhttp://www.kernel.org/linuxnoarchA큤aBaBe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855rootrootrootrootkernel-devel-puppet-3.10.0-1062.1.1.src.rpmkernel-devel-puppet    /bin/sh/usr/bin/findrpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)3.0.4-14.6.0-14.0-15.2-14.14.33.10.0-1062.1.13.10.0-1062.1.1.el7.x86_64.config/usr/src/puppet-kernels//usr/src/puppet-kernels/3.10.0-1062.1.1.el7.x86_64/-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protectioncpioxz2noarch-redhat-linux-gnudirectoryemptyutf-84f6599b39ec934630212fd1c1ac29434460c7461558ea4bd99b01f01352b7af1?7zXZ !#,] b2u jӫ`(y-!R]g<3EN9,prt#"` ?>퐻.)56S>KG*S;{pu0rlXYCX`ghE 4u4}U&;, - -̗'roCA YZkernel-devel-puppet-3.10.0-1062.4.3.noarch.rpm000066400000000000000000000155001470131746300327730ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/el-repo/RPMSkernel-devel-puppet-3.10.0-1062.4.3> )lpt 525b9dd391791ad2b93f08ee387f09d22b32229e9fd96a0f1af9b487cb0b92fad3f2dbe69030453fede013ca8e59a7d550180ca2 >5?|d & j A[rx     $,@X(GHIX Y\ ](^Kbdeflt uv,06xCkernel-devel-puppet3.10.01062.4.3Development package for building kernel modules to match the kernelThis package provides kernel headers and makefiles sufficient to build modules against the kernel package.aBbetter-converse.delivery.puppetlabs.netCentOSGPLv2CentOS BuildSystem System Environment/Kernelhttp://www.kernel.org/linuxnoarchA큤aBaBe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855rootrootrootrootkernel-devel-puppet-3.10.0-1062.4.3.src.rpmkernel-devel-puppet    /bin/sh/usr/bin/findrpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)3.0.4-14.6.0-14.0-15.2-14.14.33.10.0-1062.4.33.10.0-1062.4.3.el7.x86_64.config/usr/src/puppet-kernels//usr/src/puppet-kernels/3.10.0-1062.4.3.el7.x86_64/-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions -fstack-protector-strong -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protectioncpioxz2noarch-redhat-linux-gnudirectoryemptyutf-8b8813016b1d94eff3317d1a652c82bc6a86af95d7b8bb348121c5c07a99cd06b?7zXZ !#,] b2u jӫ`(y-!R_dKWHm__P0Q0=P N-$^c-v!^[HvG6kC0 @z4jzkbL YZpuppetlabs-puppet-789f600/acceptance/fixtures/el-repo/repodata/000077500000000000000000000000001470131746300246005ustar00rootroot000000000000000427a2b1b650922e9e7359c09be8820caa3b1ae72efef4998fd7a50fbd3a858c-primary.xml.gz000066400000000000000000000015251470131746300403770ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/el-repo/repodataVɒ6WpMqјLrHRq2!GEH@OC$%g9dE$_o]*_ztJ*!FB1ͥW훯7~UߕpSGpxOH/.AOdEA+<8`0);нC(*B(o88p%9x5?u)QP*X*Q6YDAمu4,9؎iWG\Gy4MY6Ix5I#8k]E$0RVA95햦-cy4md rρSLPn jA${?4w Oi4P::d%\XfN hQA)DʃhSJ&}:T݄^Ƀy,'~xD_xǓub@/zrJ3mm޽,MS0XR1N6(&%QeQ9)LeV*+rh ~V<6 ^3Cmz#yFYgUaz&+|+o^wKrkBqvs-+fo4KQȷPӹInslV z`N\YO(wRY=&{Qca+3J7+tctylp[wWϦ+dQ=_@N>yh/4i"[CL֜QT%Yt2:ɟ_Fs g!ͣX,EqSfGYۦۈi&M?N$p&S4)(:Tc1ߵ w`lcpe'~Arq8#JYUh 0GT#5, MM^_VnOo;iHnyxkrdn!C=ƙ}J;`Ԥ4:]d5JH ̃fX[$D4qPZH%$=tcna)1#lk M (;5͘HNGⓒ"e ϒS2 1ĵ#]e4.xnjbG]yuq7g67_1А*T%e0:tj/mi(s eZp4g@A~yןRe 4427b13c52edea24fc19776198a99611464b3c67f7828aeed8c5d20f3d8b1c02-filelists.sqlite.bz2000066400000000000000000000017151470131746300411750ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/el-repo/repodataBZh51AY&SYXoh!@8"P^B(4AL@@0CALL@hp4C@LCF42 @4444iC p4C@LCF42IhS4FzF ڞ4=Fm \ L >8,=BYtUT=EnV +ww=ϞnߐZtG;}FXpc``6af˩edļK_Զo,XA~JU\!j/] [2pRV<\%~V(eyEjc|7hW46{ =]6PWu "yByye>W4\].wgpW*Ԟ{ٙ!6ۛp{$0/WkG0-2.)pJJT(6CUY1NS-7btbԴ\ %Opʩ `PQIA F8y򐈺B,%,*+aKaLJIșLo3*LJDVDB@/y2*x@b6ڱfdτ@}~D N {4PKuVI0(Ƨ< TK'Ն)SȘO Aވ O DS-TNJ¼J#δŻټ@zAJIy)(yJb^k|m}vPNd8-]B }4E9FL Q"a8E g.1Yb{/6H^pv = d%ִje^!Fݭ($^2C3WC=9L,P?`, Z'm^ ߊ , 42L&5`Vz]"S c4750DŽknWV_s97.p!׉653202d291344674c0e6c2547647d09c2b0044ec96986b9c62f74dc49f15a3db-other.sqlite.bz2000066400000000000000000000013521470131746300400140ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/el-repo/repodataBZh51AY&SY.eh8@}ed2)LM4iL i4 SF 2Lh0AɐaFALA F&4$ O@MѓPzijyLw$uc3g'#`)XD4g$Q'i`F0c %:_]9IWw8M3 SaUtc#<;r捡$ WEl-5D Aۧzn[xUP{FhAtlͩ5f₣(FBҍ9rfc=G! hDRl @ $ 3#3( #JT%#[&n]UT9PC}q (ЃZ*\.h9=GItouʍ po%1 M9r=Oc݉ ØškާAۺ| TEű(y{oRr6F,C 1631716408 0427a2b1b650922e9e7359c09be8820caa3b1ae72efef4998fd7a50fbd3a858c c838f308b3673ee45b64f5b4f594b1428ec76c7a09045ddbcafd37cddf6c9806 1631716408 853 2885 12382dd1ca2ce49561d698430501e038a8694b64a5d69bdb7133bff1be5bd4ab ee99cce50b52fdbe9986c16a1896b3f621fd678fa3e5731f153458a9b2770aa0 1631716408 362 799 c274906bddc4277eb4a9f54ad0bfb833ae2c34209d2c8059ee187aa409886ead 537bf4c94bce5d5bf7a7ecca3e612028ba87b7bf9c2cf7c952ec902d6e3259ee 1631716408 297 493 68861daea8ff469f3418abd08697b408df11c8079b0b24178a4e2b4bd8a7102e f50da72da990487e2a53e9414b4b5fe0b47357e7ddebb8ded8450ea268d1a1f2 1631716408 2176 106496 10 4427b13c52edea24fc19776198a99611464b3c67f7828aeed8c5d20f3d8b1c02 6aa60e9b3cb91ce9240c648f2f72eeec9a9f29fa809e4fdb4d05de334e9d2f0d 1631716408 973 28672 10 653202d291344674c0e6c2547647d09c2b0044ec96986b9c62f74dc49f15a3db 44c6e7b3d018f1c291708a368223ada428e13d5dd2ecc08c56321e074006a655 1631716408 746 24576 10 puppetlabs-puppet-789f600/acceptance/fixtures/manifest_large_exported_classes_node.pp000066400000000000000000000037141470131746300314200ustar00rootroot00000000000000class foo ($bar) { @@notify { 'foo': } } @@file { "somedir/${name}_${munin_port_real}": ensure => present, content => template("munin/defaultclient.erb"), } # Collect all exported files File <<||>> # Compile the munin.conf with a local header concatenated_file { "/etc/munin/munin.conf": dir => somedir, header => "/etc/munin/munin.conf.header", } hosting_vserver_configuration { "erics": domain => "orange.co", type => "friend", context => 13, ip => "255.255.255.254", prefix => 27, admin_user => "erict", admin_user_name => "hello, its me", admin_user_email => "erict@orange.co", customer => "hello? is it me?", admin_password => file("/etc/puppet/secrets/hosting/erict_passwd"), } class davids_black_co_at { ## Create users for my parents and my grandmother hosting::user { rztt: realname => "some other rztt", uid => 2001, admin => true; same: realname => "could be same", uid => 2002; imapersontoodamnit: realname => "some one else", uid => 2003; } # Install git.black.co.at include git::daemon include git::web git::web::export { [manifests, "puppet-trunk"]: } # Provision an additional mysql database on the database server hosting::database { "fogbugz": type => mysql } # Create another VirtualHost apache2::site { "local-fogbugz": source => "puppet://$servername/files/hosting/erict/sites/local-fogbugz" } } node backuppc { # only use the smarthost $mta = ssmtp # this is a vserver on this host, so register correctly in nagios $nagios_parent = "orange.co" # I'm sharing an IP here, so those things have to have their own ports $apache2_port = 8080 $munin_port = 5008 $munin_stats_port = 8667 # default configuration include dbp # configure the backuppc server include backuppc::server } puppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/000077500000000000000000000000001470131746300233475ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/noarch/000077500000000000000000000000001470131746300246215ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/noarch/helloworld-1.0-2.noarch.rpm000066400000000000000000000047701470131746300314300ustar00rootroot00000000000000helloworld-1.0-2T>D ,0@ae626a2a7029a59dd78d78788bd44be217a766fbT{!16d>3?d     & 0 D  $(GHIXY\]^"bFdefltuv(w\xpyChelloworld1.02Hello WorldThis is a test package for Puppet package providers (Converted from a deb package by alien version 8.95.)^[nice-martyrdom.delivery.puppetlabs.net Debiansee /usr/share/doc/helloworld/copyrightConverted/baselinuxnoarch, AAAA^[^j>l^j>l^j>^j>febebfea80911bf011b3a95539e518a90f33b54fc43e03884005170b38a17f1drootrootrootrootrootrootrootrootrootroothelloworld-1.0-2.src.rpmhelloworld@    /bin/shrpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)3.0.4-14.6.0-14.0-15.2-14.11.31.0-2usrlocalbinhello_world.sh//usr//usr/local//usr/local/bin/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=genericcpioxz2noarch-redhat-linux-gnudirectoryPOSIX shell script, ASCII text executableR?7zXZ !#,] b2u jՉ`(}P$7D'E }"Q9(Z،&cX07E#i!=mAiA9v;׊/]lng^Gv\pVo4V'^X==G| >{ۀˍpL!ҧfA5RP#Ex t"oX YZpuppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/noarch/helloworld-1.19-2.noarch.rpm000066400000000000000000000047701470131746300315220ustar00rootroot00000000000000helloworld-1.19-2T>D ,0@a4bf549d3d278f460dde0dbd7d9eb9006c86496d'X3an$w* >3?d     & 0 D  $(GHIXY\]^"bFdefltuv(w\xpyChelloworld1.192Hello WorldThis is a test package for Puppet package providers (Converted from a deb package by alien version 8.95.)^enice-martyrdom.delivery.puppetlabs.net Debiansee /usr/share/doc/helloworld/copyrightConverted/baselinuxnoarch- AAAA^e^j>l^j>l^j>^j>febebfea80911bf011b3a95539e518a90f33b54fc43e03884005170b38a17f1drootrootrootrootrootrootrootrootrootroothelloworld-1.19-2.src.rpmhelloworld@    /bin/shrpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)3.0.4-14.6.0-14.0-15.2-14.11.31.19-2usrlocalbinhello_world.sh//usr//usr/local//usr/local/bin/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=genericcpioxz2noarch-redhat-linux-gnudirectoryPOSIX shell script, ASCII text executableR?7zXZ !#,] b2u jՉ`(}P$7:Xƪ&2@~d0ԟ`ۀ.+/]U\4ZI_:PƝF G7ɤ9{AB[ - )bgL԰&T>gp}'i$Y#_H^/ۦ;H5隓H:@q}9PB"4v$V"z' moX YZpuppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/noarch/helloworld-2.0-2.noarch.rpm000066400000000000000000000047701470131746300314310ustar00rootroot00000000000000helloworld-2.0-2T>D ,0@c8962659bff48735fc6fa5bb2c8f913aba173fdd KJt>3?d     & 0 D  $(GHIXY\]^"bFdefltuv(w\xpyChelloworld2.02Hello WorldThis is a test package for Puppet package providers (Converted from a deb package by alien version 8.95.)^nnice-martyrdom.delivery.puppetlabs.net Debiansee /usr/share/doc/helloworld/copyrightConverted/baselinuxnoarch, AAAA^n^j>l^j>l^j>^j>febebfea80911bf011b3a95539e518a90f33b54fc43e03884005170b38a17f1drootrootrootrootrootrootrootrootrootroothelloworld-2.0-2.src.rpmhelloworld@    /bin/shrpmlib(CompressedFileNames)rpmlib(FileDigests)rpmlib(PayloadFilesHavePrefix)rpmlib(PayloadIsXz)3.0.4-14.6.0-14.0-15.2-14.11.32.0-2usrlocalbinhello_world.sh//usr//usr/local//usr/local/bin/-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=genericcpioxz2noarch-redhat-linux-gnudirectoryPOSIX shell script, ASCII text executableR?7zXZ !#,] b2u jՉ`(}P$7:ZEnY|9*MpO^#.%aI\}tZ,-iOx-cޢ{lOE#ͺD!1/m xv(ٕ+Qs{Ņ]GV:E -@Uvcb:Y2펹ErPkHxڭ)oX YZpuppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/repodata/000077500000000000000000000000001470131746300251465ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/repodata/filelists.xml.gz000066400000000000000000000005321470131746300303050ustar00rootroot00000000000000唽n <b1S'h犟`?mʼnHJZνڞ0.Ԙ&)FLg]fS΃w4c]O&eդȧ^A.Q9[cMA i(XV Ȅ-)FA!n#5}CQQrĝ5 fs.M6d;<.@2s*G[n 4(lCZc)ZF 2Lg\\+δ,fTgʒ.Vp. Keȷy{#SShwpuppetlabs-puppet-789f600/acceptance/fixtures/sles-repo/repodata/other.xml.gz000066400000000000000000000004411470131746300274270ustar00rootroot00000000000000n } }%B=&j Qn})aiR}_#7ud4߆[.g} C 9Zav”}Re\=%]h_ CBRD PqGk(ڧɢÕ je L)@|K*yƻyJ<+Z8t9ɘB:6K\`AIAT 1kOZ˜A7һɚoS97n6 PSc.԰pXJΌV^ϔpVxpVi؋br lB!Xp9ABi|cb' 1586872174 57a44da7ea9c26d4f438d6ca5da3c561acfaabe1 4de6823d4c547a4d0126368662a25086ebba0b48 1586872175 346 1143 e8a80b739fad3deba689620da7947ea0e4bb8ec6 438d8c0a6507319e488dabcb95a37e6e3a984086 1586872175 289 563 76eab89dc047884727aa27a4523e21728f356eb1 803b916a92d58ee369063232de96c62125e1e483 1586872175 817 3654 puppetlabs-puppet-789f600/acceptance/lib/000077500000000000000000000000001470131746300203335ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/lib/acceptance_spec_helper.rb000066400000000000000000000002261470131746300253170ustar00rootroot00000000000000require 'fileutils' dir = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift dir RSpec.configure do |config| config.mock_with :mocha end puppetlabs-puppet-789f600/acceptance/lib/helper.rb000066400000000000000000000001201470131746300221300ustar00rootroot00000000000000$LOAD_PATH << File.expand_path(File.dirname(__FILE__)) require 'beaker-puppet' puppetlabs-puppet-789f600/acceptance/lib/puppet/000077500000000000000000000000001470131746300216505ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/000077500000000000000000000000001470131746300237365ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/agent_fqdn_utils.rb000066400000000000000000000007011470131746300276070ustar00rootroot00000000000000module Puppet module Acceptance module AgentFqdnUtils @@hostname_to_fqdn = {} # convert from an Beaker::Host (agent) to the systems fqdn as returned by facter def agent_to_fqdn(agent) unless @@hostname_to_fqdn.has_key?(agent.hostname) @@hostname_to_fqdn[agent.hostname] = on(agent, facter('networking.fqdn')).stdout.chomp end @@hostname_to_fqdn[agent.hostname] end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/aix_util.rb000066400000000000000000000150731470131746300261070ustar00rootroot00000000000000module Puppet module Acceptance module AixUtil def to_kv_array(attributes) attributes.map { |attribute, value| "#{attribute}=#{value}" } end def assert_object_attributes_on(host, object_get, object, expected_attributes) host.send(object_get, object) do |result| actual_attrs_kv_pairs = result.stdout.chomp.split(' ')[(1..-1)] actual_attrs = actual_attrs_kv_pairs.map do |kv_pair| attribute, value = kv_pair.split('=') next nil unless value [attribute, value] end.compact.to_h expected_attributes.each do |attribute, value| attribute_str = "attributes[#{object}][#{attribute}]" actual_value = actual_attrs[attribute] assert_match( /\A#{value}\z/, actual_value, "EXPECTED: #{attribute_str} = \"#{value}\", ACTUAL: #{attribute_str} = \"#{actual_value}\"" ) end end end def assert_puppet_changed_object_attributes(result, object_resource, object, changed_attributes) stdout = result.stdout.chomp changed_attributes.each do |attribute, value| prefix = /#{object_resource}\[#{object}\].*attributes changed.*/ attribute_str = "attributes[#{object}][#{attribute}]" assert_match( /#{prefix}#{attribute}=#{value}/, stdout, "Puppet did not indicate that #{attribute_str} changed to #{value}" ) end end def object_resource_manifest(object_resource, object, params) params_str = params.map do |param, value| value_str = value.to_s value_str = "\"#{value_str}\"" if value.is_a?(String) " #{param} => #{value_str}" end.join(",\n") <<-MANIFEST #{object_resource} { '#{object}': #{params_str} } MANIFEST end def run_attribute_management_tests(object_resource, id_property, initial_attributes, changed_attributes) object_get = "#{object_resource}_get".to_sym object_absent = "#{object_resource}_absent".to_sym name = "obj" teardown do agents.each { |agent| agent.send(object_absent, name) } end current_attributes = initial_attributes.dup agents.each do |agent| agent.send(object_absent, name) # We extract the code for this step as a lambda because we will be checking # for this case (1) Before the object has been created and (2) After the # object has been created (towards the end). We do this because in (1), Puppet # does not trigger the property setters after creating the object, while in (2) # it does. These are two different scenarios that we want to check. step_run_errors_when_property_is_passed_as_attribute = lambda do manifest = object_resource_manifest( object_resource, name, attributes: current_attributes.merge({ 'id' => '15' }) ) apply_manifest_on(agent, manifest) do |result| assert_match(/Error:.*'#{id_property}'.*'id'/, result.stderr, "specifying a Puppet property as part of an AIX attribute should have errored, but received #{result.stderr}") end end step "Ensure that Puppet errors if a Puppet property is passed in as an AIX attribute when creating the #{object_resource}" do step_run_errors_when_property_is_passed_as_attribute.call end step "Ensure that the #{object_resource} can be created with the specified attributes" do manifest = object_resource_manifest( object_resource, name, ensure: :present, attributes: to_kv_array(current_attributes) ) apply_manifest_on(agent, manifest) assert_object_attributes_on(agent, object_get, name, current_attributes) end step "Ensure that Puppet noops when the specified attributes are already set" do manifest = object_resource_manifest( object_resource, name, attributes: to_kv_array(current_attributes) ) apply_manifest_on(agent, manifest, catch_changes: true) end # Remember the changed attribute's old values old_attributes = current_attributes.select { |k, _| changed_attributes.keys.include?(k) } step "Ensure that Puppet updates only the specified attributes and nothing else" do current_attributes = current_attributes.merge(changed_attributes) manifest = object_resource_manifest( object_resource, name, attributes: to_kv_array(current_attributes) ) apply_manifest_on(agent, manifest) do |result| assert_puppet_changed_object_attributes( result, object_resource.capitalize, name, changed_attributes ) end assert_object_attributes_on(agent, object_get, name, current_attributes) end step "Ensure that Puppet accepts a hash for the attributes property" do # We want to see if Puppet will do something with the attributes property # when we pass it in as a hash so that it does not just pass validation # and end up noop-ing. Let's set one of our attributes back to its old # value in order to simulate an actual change. attribute = old_attributes.keys.first old_value = old_attributes.delete(attribute) current_attributes[attribute] = old_value manifest = object_resource_manifest( object_resource, name, attributes: current_attributes ) apply_manifest_on(agent, manifest) assert_object_attributes_on(agent, object_get, name, current_attributes) end step "Ensure that `puppet resource #{object_resource}` outputs valid Puppet code" do on(agent, puppet("resource #{object_resource} #{name}")) do |result| manifest = result.stdout.chomp apply_manifest_on(agent, manifest) end end step "Ensure that Puppet errors if a Puppet property is passed in as an AIX attribute after #{object_resource} has been created" do step_run_errors_when_property_is_passed_as_attribute.call end end end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/classifier_utils.rb000066400000000000000000000163571470131746300276430ustar00rootroot00000000000000require 'httparty' require 'tempfile' require 'stringio' require 'uuidtools' require 'json' require 'pp' module Puppet module Acceptance module ClassifierUtils DEFAULT_GROUP_ID = "00000000-0000-4000-8000-000000000000" SSL_PORT = 4433 PREFIX = "/classifier-api" # Keep track of our local tmpdirs for cleanup def self.tmpdirs @classifier_utils_tmpdirs ||= [] end # PE creates a "Production environment" group during installation which # all nodes are a member of by default. This method just looks up this # group and returns its uuid so that other methods may reference it. def get_production_environment_group_uuid step "Get classifier groups so we can locate the 'Production environment' group" response = classifier_handle.get("/v1/groups") assert_equal(200, response.code, "Unable to get classifer groups: #{response.body}") groups_json = response.body groups = JSON.parse(groups_json) if production_environment = groups.find { |g| g['name'] == 'Production environment' } production_environment['id'] else nil end end # Create a Classifier Group which by default will apply to all of the passed # nodes. The Group will merge in the passed group_hash which will be converted # into the json body for a Classifier PUT /v1/groups/:id request. # # A teardown body is registered to delete the created group at the end of the test. # # @returns String the created uuid for the group. def create_group_for_nodes(nodes, group_hash) group_uuid = UUIDTools::UUID.random_create() response = nil teardown do step "Deleting group #{group_uuid}" do response = classifier_handle.delete("/v1/groups/#{group_uuid}") assert_equal(204, response.code, "Failed to delete group #{group_uuid}, #{response.code}:#{response.body}") end if response && response.code == 201 end teardown do step "Cleaning up classifier certs on test host" do cleanup_local_classifier_certs end end hostnames = nodes.map { |n| n.hostname } step "Add group #{group_uuid} for #{hostnames.join(", ")}" rule = hostnames.inject(["or"]) do |r,name| r << ["~", "name", name] r end # In order to override the environment for test nodes, we need the # groups we create to be a child of this "Production environment" group, # otherwise we get a classification error from the conflicting groups. parent = get_production_environment_group_uuid || Puppet::Acceptance::ClassifierUtils::DEFAULT_GROUP_ID body = { "description" => "A classification group for the following acceptance test nodes: (#{hostnames.join(", ")})", "parent" => parent, "rule" => rule, "classes" => {} }.merge group_hash response = classifier_handle.put("/v1/groups/#{group_uuid}", :body => body.to_json) assert_equal(201, response.code, "Unexpected response code: #{response.code}, #{response.body}") return group_uuid end # Creates a group which allows the given nodes to specify their own environments. # Will be torn down at the end of the test. def classify_nodes_as_agent_specified(nodes) create_group_for_nodes(nodes, { "name" => "Agent Specified Test Nodes", "environment" => "agent-specified", "environment_trumps" => true, "description" => "The following acceptance suite nodes (#{nodes.map { |n| n.hostname }.join(", ")}) expect to be able to specify their environment for tesing purposes.", }) end def classify_nodes_as_agent_specified_if_classifer_present classifier_node = false begin classifier_node = find_only_one(:classifier) rescue Beaker::DSL::Outcomes::FailTest end if classifier_node || master.is_pe? classify_nodes_as_agent_specified(agents) end end def classifier_host find_only_one(:classifier) rescue Beaker::DSL::Outcomes::FailTest # fallback to master since currently the sqautils genconfig does not recognize # a classifier role. master end def master_cert @master_cert ||= on(master, "cat `puppet config print hostcert`", :silent => true).stdout end def master_key @master_key ||= on(master, "cat `puppet config print hostprivkey`", :silent => true).stdout end def master_ca_cert_file unless @ca_cert_file ca_cert = on(master, "cat `puppet config print localcacert`", :silent => true).stdout cert_dir = Dir.mktmpdir("pe_classifier_certs") Puppet::Acceptance::ClassifierUtils.tmpdirs << cert_dir @ca_cert_file = File.join(cert_dir, "cacert.pem") # RFC 1421 states PEM is 7-bit ASCII https://tools.ietf.org/html/rfc1421 File.open(@ca_cert_file, "w:ASCII") do |f| f.write(ca_cert) end end @ca_cert_file end def cleanup_local_classifier_certs Puppet::Acceptance::ClassifierUtils.tmpdirs.each do |d| FileUtils.rm_rf(d) end end def clear_classifier_utils_cache @master_cert = nil @master_key = nil @ca_cert_file = nil @classifier_handle = nil end def classifier_handle(options = {}) unless @classifier_handle server = options[:server] || classifier_host.reachable_name port = options[:port] || SSL_PORT prefix = options[:prefix] || PREFIX cert = options[:cert] || master_cert key = options[:key] || master_key ca_cert_file = options[:ca_cert_file] || master_ca_cert_file logger = options[:logger] || self.logger # HTTParty performs a lot of configuration at the class level. # This is inconvenient for our needs because we don't have the # server/cert info available at the time the class is loaded. I'm # sidestepping this by generating an anonymous class on the fly when # the test code actually requests a handle to the classifier. @classifier_handle = Class.new do include HTTParty extend Classifier @debugout = StringIO.new @logger = logger base_uri("https://#{server}:#{port}#{prefix}") debug_output(@debugout) headers({'Content-Type' => 'application/json'}) pem(cert + key) ssl_ca_file(ca_cert_file) end end @classifier_handle end # Handle logging module Classifier [:head, :get, :post, :put, :delete].each do |method| define_method(method) do |*args, &block| log_output do super(*args, &block) end end end private # Ensure that the captured debugging output is logged to Beaker. def log_output yield ensure @debugout.rewind @debugout.each_line { |l| @logger.info(l) } @debugout.truncate(0) end end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/classifier_utils_spec.rb000066400000000000000000000042501470131746300306420ustar00rootroot00000000000000require File.join(File.dirname(__FILE__),'../../acceptance_spec_helper.rb') require 'puppet/acceptance/classifier_utils' require 'stringio' require 'beaker' module ClassifierUtilsSpec describe 'ClassifierUtils' do class ATestCase < Beaker::TestCase include Puppet::Acceptance::ClassifierUtils attr_accessor :logger, :hosts def initialize @logger = Logger.new @hosts = [] end def logger @logger end def teardown end class Logger attr_reader :destination def initialize @destination = StringIO.new end def info(log) @destination << (log) end end end let(:testcase) { ATestCase.new } let(:handle) { testcase.classifier_handle( :server => 'foo', :cert => 'cert', :key => 'key', :ca_cert_file => 'file' ) } it "provides a handle to the classifier service" do handle.expects(:perform_request).with(Net::HTTP::Get, '/hi', {}) handle.get('/hi') end it "logs output from the http connection attempt" do TCPSocket.expects(:open).raises('no-connection') OpenSSL::X509::Certificate.expects(:new).with('certkey').returns(stub('cert')) OpenSSL::PKey::RSA.expects(:new).with('certkey', nil).returns(stub('key')) expect { handle.get('/hi') }.to raise_error('no-connection') expect(testcase.logger.destination.string).to match(/opening connection to foo/) end it "creates an agent-specified environment group for a passed set of nodes" do nodes = [ stub_everything('master', :hostname => 'abcmaster', :[] => ['master'] ), stub_everything('agent', :hostname => 'defagent', :[] => ['agent'] ), ] testcase.hosts = nodes uuid = nil handle.expects(:perform_request).with do |method,url,body_hash| expect(method).to eq(Net::HTTP::Put) test_regex = %r{/v1/groups/(\w+-\w+-\w+-\w+-\w+)} md = test_regex.match(url) expect(uuid = md[1]).to_not be_nil expect(body_hash[:body]).to match(/environment[^:]*:[^:]*agent-specified/) end.returns( stub_everything('response', :code => 201)) expect(testcase.classify_nodes_as_agent_specified(nodes).to_s).to eq(uuid) end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/common_utils.rb000066400000000000000000000034351470131746300270000ustar00rootroot00000000000000module Puppet module Acceptance module BeakerUtils # TODO: This should be added to Beaker def assert_matching_arrays(expected, actual, message = "") assert_equal(expected.sort, actual.sort, message) end end module PackageUtils def package_present(host, package, version = nil) host.install_package(package, '', version) end def package_absent(host, package, cmdline_args = '', opts = {}) host.uninstall_package(package, cmdline_args, opts) end end module CommandUtils def ruby_command(host) "env PATH=\"#{host['privatebindir']}:${PATH}\" ruby" end module_function :ruby_command def gem_command(host, type='aio') if type == 'aio' if host['platform'] =~ /windows/ "env PATH=\"#{host['privatebindir']}:${PATH}\" cmd /c gem" else "env PATH=\"#{host['privatebindir']}:${PATH}\" gem" end else on(host, 'which gem').stdout.chomp end end module_function :gem_command end module ManifestUtils def resource_manifest(resource, title, params = {}) params_str = params.map do |param, value| # This is not quite correct for all parameter values, # but it is good enough for most purposes. value_str = value.to_s value_str = "\"#{value_str}\"" if value.is_a?(String) " #{param} => #{value_str}" end.join(",\n") <<-MANIFEST #{resource} { '#{title}': #{params_str} } MANIFEST end def file_manifest(path, params = {}) resource_manifest('file', path, params) end def user_manifest(username, params = {}) resource_manifest('user', username, params) end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/environment_utils.rb000066400000000000000000000363661470131746300300650ustar00rootroot00000000000000require 'puppet/acceptance/module_utils' module Puppet module Acceptance module EnvironmentUtils include Puppet::Acceptance::ModuleUtils # Generate puppet manifest for the creation of an environment with # the given modulepath and manifest and env_name. The created environment # will have on testing_mod module, and manifest site.pp which includes it. # # @param options [Hash] # @option options [String] :modulepath Modules directory # @option options [String] :manifest Manifest directory # @option options [String] :env_name Environment name # @return [String] Puppet manifest to create the environment files def generate_environment(options) modulepath = options[:modulepath] manifestpath = options[:manifestpath] env_name = options[:env_name] <<-MANIFEST_SNIPPET file { ################################################### # #{env_name} #{generate_module("testing_mod", env_name, modulepath)} "#{manifestpath}":; "#{manifestpath}/site.pp": ensure => file, mode => "0640", content => ' notify { "in #{env_name} site.pp": } include testing_mod ' ; } MANIFEST_SNIPPET end # Generate one module's manifest code. def generate_module(module_name, env_name, modulepath) <<-MANIFEST_SNIPPET "#{modulepath}":; "#{modulepath}/#{module_name}":; "#{modulepath}/#{module_name}/manifests":; "#{modulepath}/#{module_name}/manifests/init.pp": ensure => file, mode => "0640", content => 'class #{module_name} { notify { "include #{env_name} #{module_name}": } }' ; MANIFEST_SNIPPET end # Default, legacy, dynamic and directory environments # using generate_manifest(), all rooted in testdir. # # @param [String] testdir path to the temp directory which will be the confdir all # the environments live in # @return [String] Puppet manifest to generate all of the environment files. def environment_manifest(testdir) <<-MANIFEST File { ensure => directory, owner => #{master.puppet['user']}, group => #{master.puppet['group']}, mode => "0750", } file { "#{testdir}": } #{generate_environment( :modulepath => "#{testdir}/modules", :manifestpath => "#{testdir}/manifests", :env_name => "default environment")} #{generate_environment( :modulepath => "#{testdir}/testing-modules", :manifestpath => "#{testdir}/testing-manifests", :env_name => "legacy testing environment")} file { "#{testdir}/dynamic":; "#{testdir}/dynamic/testing":; } #{generate_environment( :modulepath => "#{testdir}/dynamic/testing/modules", :manifestpath => "#{testdir}/dynamic/testing/manifests", :env_name => "dynamic testing environment")} file { "#{testdir}/environments":; "#{testdir}/environments/testing":; } #{generate_environment( :modulepath => "#{testdir}/environments/testing/modules", :manifestpath => "#{testdir}/environments/testing/manifests", :env_name => "directory testing environment")} file { "#{testdir}/environments/testing_environment_conf":; } #{generate_environment( :modulepath => "#{testdir}/environments/testing_environment_conf/nonstandard-modules", :manifestpath => "#{testdir}/environments/testing_environment_conf/nonstandard-manifests", :env_name => "directory testing with environment.conf")} file { "#{testdir}/environments/testing_environment_conf/environment.conf": ensure => file, mode => "0640", content => ' modulepath = nonstandard-modules:$basemodulepath manifest = nonstandard-manifests config_version = local-version.sh ' } file { "#{testdir}/environments/testing_environment_conf/local-version.sh": ensure => file, mode => "0640", content => '#! /usr/bin/env bash echo "local testing_environment_conf"' ; } ################### # Services file { "#{testdir}/services":; "#{testdir}/services/testing":; #{generate_module('service_mod', "service testing environment", "#{testdir}/services/testing/modules")} } ####################### # Config version script file { "#{testdir}/static-version.sh": ensure => file, mode => "0640", content => '#! /usr/bin/env bash echo "static"' ; } MANIFEST end def get_directory_hash_from(host, path) dir_hash = {} on(host, "ls #{path}") do |result| result.stdout.split.inject(dir_hash) do |hash,f| hash[f] = "#{path}/#{f}" hash end end dir_hash end def safely_shadow_directory_contents_and_yield(host, original_path, new_path, &block) original_files = get_directory_hash_from(host, original_path) new_files = get_directory_hash_from(host, new_path) conflicts = original_files.keys & new_files.keys step "backup original files" do conflicts.each do |c| on(host, "mv #{original_files[c]} #{original_files[c]}.bak") end end step "shadow original files with temporary files" do new_files.each do |name,full_path_name| on(host, "cp -R #{full_path_name} #{original_path}/#{name}") end end new_file_list = new_files.keys.map { |name| "#{original_path}/#{name}" }.join(' ') step "open permissions to 755 on all temporary files copied into working dir and set ownership" do on(host, "chown -R #{host.puppet['user']}:#{host.puppet['group']} #{new_file_list}") on(host, "chmod -R 755 #{new_file_list}") end if host.check_for_command("selinuxenabled") result = on(host, "selinuxenabled", :acceptable_exit_codes => [0,1]) if result.exit_code == 0 step "mirror selinux contexts" do context = on(host, "matchpathcon #{original_path}").stdout.chomp.split(' ')[1] on(host, "chcon -R #{context} #{new_file_list}") end end end yield ensure step "clear out the temporary files" do files_to_delete = new_files.keys.map { |name| "#{original_path}/#{name}" } on(host, "rm -rf #{files_to_delete.join(' ')}") end step "move the shadowed files back to their original places" do conflicts.each do |c| on(host, "mv #{original_files[c]}.bak #{original_files[c]}") end end end # Stand up a puppet master on the master node with the given master_opts # using the passed envdir as the source of the puppet environment files, # and passed confdir as the directory to use for the temporary # puppet.conf. It then runs through a series of environment tests for the # passed environment and returns a hashed structure of the results. # # @return [Hash>] Hash of # Beaker::Hosts for each agent run keyed to a hash of Beaker::Result # objects keyed by each subtest that was performed. def use_an_environment(environment, description, master_opts, envdir, confdir, options = {}) master_puppet_conf = master_opts.dup # shallow clone results = {} safely_shadow_directory_contents_and_yield(master, puppet_config(master, 'codedir', section: 'master'), envdir) do config_print = options[:config_print] directory_environments = options[:directory_environments] with_puppet_running_on(master, master_puppet_conf, confdir) do agents.each do |agent| agent_results = results[agent] = {} step "puppet agent using #{description} environment" args = "-t", "--server", master args << ["--environment", environment] if environment # Test agents configured to use directory environments (affects environment # loading on the agent, especially with regards to requests/node environment) args << "--environmentpath='$confdir/environments'" if directory_environments && agent != master on(agent, puppet("agent", *args), :acceptable_exit_codes => (0..255)) do |result| agent_results[:puppet_agent] = result end args = ["--trace"] args << ["--environment", environment] if environment step "print puppet config for #{description} environment" on(master, puppet(*(["config", "print", "basemodulepath", "modulepath", "manifest", "config_version", config_print] + args)), :acceptable_exit_codes => (0..255)) do |result| agent_results[:puppet_config] = result end step "puppet apply using #{description} environment" on(master, puppet(*(["apply", '-e', '"include testing_mod"'] + args)), :acceptable_exit_codes => (0..255)) do |result| agent_results[:puppet_apply] = result end end end end return results end # For each Beaker::Host in the results Hash, generates a chart, comparing # the expected exit code and regexp matches from expectations to the # Beaker::Result.output for a particular command that was executed in the # environment. Outputs either 'ok' or text highlighting the errors, and # returns false if any errors were found. # # @param [Hash>] results # @param [Hash Integer,Array}>] expectations # @return [Array] Returns an empty array of there were no failures, or an # Array of failed cases. def review_results(results, expectations) failed = [] results.each do |agent, agent_results| divider = "-" * 79 logger.info divider logger.info "For: (#{agent.name}) #{agent}" logger.info divider agent_results.each do |testname, execution_results| expected_exit_code = expectations[testname][:exit_code] match_tests = expectations[testname][:matches] || [] not_match_tests = expectations[testname][:does_not_match] || [] expect_failure = expectations[testname][:expect_failure] notes = expectations[testname][:notes] errors = [] if execution_results.exit_code != expected_exit_code errors << "To exit with an exit code of '#{expected_exit_code}', instead of '#{execution_results.exit_code}'" end match_tests.each do |regexp| if execution_results.output !~ regexp errors << "#{errors.empty? ? "To" : "And"} match: #{regexp}" end end not_match_tests.each do |regexp| if execution_results.output =~ regexp errors << "#{errors.empty? ? "Not to" : "And not"} match: #{regexp}" end end error_msg = "Expected the output:\n#{execution_results.output}\n#{errors.join("\n")}" unless errors.empty? case_failed = case when errors.empty? && expect_failure then 'ok - failed as expected' when errors.empty? && !expect_failure then 'ok' else '*UNEXPECTED FAILURE*' end logger.info "#{testname}: #{case_failed}" if case_failed == 'ok - failed as expected' logger.info divider logger.info "Case is known to fail as follows:\n#{execution_results.output}\n" elsif case_failed == '*UNEXPECTED FAILURE*' failed << "Unexpected failure for #{testname}" logger.info divider logger.info "#{error_msg}" end logger.info("------\nNotes: #{notes}") if notes logger.info divider end end return failed end def assert_review(review) failures = [] review.each do |failed| if !failed.empty? problems = "Problems in the output reported above:\n #{failed}" logger.warn(problems) failures << problems end end assert failures.empty?, "Failed Review:\n\n#{failures.join("\n")}\n" end # generate a random string of 6 letters and numbers. NOT secure def random_string [*('a'..'z'),*('0'..'9')].shuffle[0,8].join end private :random_string # if the first test to call this has changed the environmentpath, this will cause trouble # maybe not the best idea to memoize this? def environmentpath @@memoized_environmentpath ||= master.puppet['environmentpath'] end module_function :environmentpath # create a tmpdir to hold a temporary environment bound by puppet environment naming restrictions # symbolically link environment into environmentpath # we can't use the temp_file utils in our own lib because host.tmpdir violates puppet's naming requirements # in rare cases we want to do this on agents when testing things that use the default manifest def mk_tmp_environment_with_teardown(host, environment) # add the tmp_environment to a set to ensure no collisions @@tmp_environment_set ||= Set.new deadman = 100; loop_num = 0 while @@tmp_environment_set.include?(tmp_environment = environment.downcase + '_' + random_string) do break if (loop_num = loop_num + 1) > deadman end @@tmp_environment_set << tmp_environment tmpdir = File.join('','tmp',tmp_environment) on host, "mkdir -p #{tmpdir}/manifests #{tmpdir}/modules; chmod -R 755 #{tmpdir}" # register teardown to remove the link below teardown do on host, "rm -rf #{File.join(environmentpath,tmp_environment)}" end # WARNING: this won't work with filesync (symlinked environments are not supported) on host, "mkdir -p #{environmentpath}; ln -sf #{tmpdir} #{File.join(environmentpath,tmp_environment)}" return tmp_environment end module_function :mk_tmp_environment_with_teardown # create sitepp in a tmp_environment as created by mk_tmp_environment_with_teardown def create_sitepp(host, tmp_environment, file_content) file_path = File.join('','tmp',tmp_environment,'manifests','site.pp') create_remote_file(host, file_path, file_content) on host, "chmod -R 755 #{file_path}" end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/environment_utils_spec.rb000066400000000000000000000134321470131746300310640ustar00rootroot00000000000000require File.join(File.dirname(__FILE__),'../../acceptance_spec_helper.rb') require 'puppet/acceptance/environment_utils' module EnvironmentUtilsSpec describe 'EnvironmentUtils' do class ATestCase include Puppet::Acceptance::EnvironmentUtils def step(str) yield end def on(host, command, options = nil) stdout = host.do(command, options) yield TestResult.new(stdout) if block_given? end end class TestResult attr_accessor :stdout def initialize(stdout) self.stdout = stdout end end class TestHost attr_accessor :did, :directories, :attributes def initialize(directories, attributes = {}) self.directories = directories self.did = [] self.attributes = attributes end def do(command, options) did << (options.nil? ? command : [command, options]) case command when /^ls (.*)/ then directories[$1] end end def [](param) attributes[param] end end let(:testcase) { ATestCase.new } let(:host) { TestHost.new(directory_contents, 'user' => 'root', 'group' => 'puppet') } let(:directory_contents) do { '/etc/puppetlabs/puppet' => 'foo bar baz widget', '/tmp/dir' => 'foo dingo bar thing', } end it "runs the block of code" do ran_code = false testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') do ran_code = true end expect(ran_code).to be true expect(host.did).to eq([ "ls /etc/puppetlabs/puppet", "ls /tmp/dir", "mv /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/foo.bak", "mv /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/bar.bak", "cp -R /tmp/dir/foo /etc/puppetlabs/puppet/foo", "cp -R /tmp/dir/dingo /etc/puppetlabs/puppet/dingo", "cp -R /tmp/dir/bar /etc/puppetlabs/puppet/bar", "cp -R /tmp/dir/thing /etc/puppetlabs/puppet/thing", "chown -R root:puppet /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", "chmod -R 770 /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", "rm -rf /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", "mv /etc/puppetlabs/puppet/foo.bak /etc/puppetlabs/puppet/foo", "mv /etc/puppetlabs/puppet/bar.bak /etc/puppetlabs/puppet/bar" ]) end it "backs up the original items that are shadowed by tmp items" do testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {} expect(host.did.grep(%r{mv /etc/puppetlabs/puppet/\w+ })).to eq([ "mv /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/foo.bak", "mv /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/bar.bak", ]) end it "copies in all the tmp items into the working dir" do testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {} expect(host.did.grep(%r{cp})).to eq([ "cp -R /tmp/dir/foo /etc/puppetlabs/puppet/foo", "cp -R /tmp/dir/dingo /etc/puppetlabs/puppet/dingo", "cp -R /tmp/dir/bar /etc/puppetlabs/puppet/bar", "cp -R /tmp/dir/thing /etc/puppetlabs/puppet/thing", ]) end it "opens the permissions on all copied files to 770 and sets ownership based on host settings" do testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {} expect(host.did.grep(%r{ch(mod|own)})).to eq([ "chown -R root:puppet /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", "chmod -R 770 /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", ]) end it "deletes all the tmp items from the working dir" do testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {} expect(host.did.grep(%r{rm})).to eq([ "rm -rf /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", ]) end it "replaces the original items that had been shadowed into the working dir" do testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') {} expect(host.did.grep(%r{mv /etc/puppetlabs/puppet/\w+\.bak})).to eq([ "mv /etc/puppetlabs/puppet/foo.bak /etc/puppetlabs/puppet/foo", "mv /etc/puppetlabs/puppet/bar.bak /etc/puppetlabs/puppet/bar" ]) end it "always cleans up, even if the code we yield to raises an error" do expect do testcase.safely_shadow_directory_contents_and_yield(host, '/etc/puppetlabs/puppet', '/tmp/dir') do raise 'oops' end end.to raise_error('oops') expect(host.did).to eq([ "ls /etc/puppetlabs/puppet", "ls /tmp/dir", "mv /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/foo.bak", "mv /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/bar.bak", "cp -R /tmp/dir/foo /etc/puppetlabs/puppet/foo", "cp -R /tmp/dir/dingo /etc/puppetlabs/puppet/dingo", "cp -R /tmp/dir/bar /etc/puppetlabs/puppet/bar", "cp -R /tmp/dir/thing /etc/puppetlabs/puppet/thing", "chown -R root:puppet /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", "chmod -R 770 /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", "rm -rf /etc/puppetlabs/puppet/foo /etc/puppetlabs/puppet/dingo /etc/puppetlabs/puppet/bar /etc/puppetlabs/puppet/thing", "mv /etc/puppetlabs/puppet/foo.bak /etc/puppetlabs/puppet/foo", "mv /etc/puppetlabs/puppet/bar.bak /etc/puppetlabs/puppet/bar" ]) end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/i18n_utils.rb000066400000000000000000000031371470131746300262660ustar00rootroot00000000000000module Puppet module Acceptance module I18nUtils # try to enable the locale's for a given language on the agent and return the preferred language name # # @param agent [string] the agent to check the locale configuration on # @param language [string] the language attempt to configure if needed # @return language [string] the language string to use on the agent node, will return nil if not available def enable_locale_language(agent, language) if agent['platform'] =~ /ubuntu/ on(agent, 'locale -a') do |locale_result| if locale_result.stdout !~ /#{language}/ on(agent, "locale-gen --lang #{language}") end end elsif agent['platform'] =~ /debian/ on(agent, 'locale -a') do |locale_result| if locale_result.stdout !~ /#{language}/ on(agent, "cp /etc/locale.gen /etc/locale.gen.orig ; sed -e 's/# #{language}/#{language}/' /etc/locale.gen.orig > /etc/locale.gen") on(agent, 'locale-gen') end end end return language_name(agent, language) end # figure out the preferred language string for the requested language if the language is configured on the system def language_name(agent, language) step "PLATFORM #{agent['platform']}" on(agent, 'locale -a') do |locale_result| ["#{language}.utf8", "#{language}.UTF-8", language].each do |lang| return lang if locale_result.stdout =~ /#{lang}/ end end return nil end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/i18ndemo_utils.rb000066400000000000000000000030171470131746300271300ustar00rootroot00000000000000module Puppet module Acceptance module I18nDemoUtils require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils I18NDEMO_NAME = "i18ndemo" I18NDEMO_MODULE_NAME = "eputnam-#{I18NDEMO_NAME}" def configure_master_system_locale(language) language = enable_locale_language(master, language) fail_test("puppet server machine is missing #{language} locale. help...") if language.nil? on(master, "localectl set-locale LANG=#{language}") on(master, "service #{master['puppetservice']} restart") end def reset_master_system_locale language = language_name(master, 'en_US') || 'en_US' on(master, "localectl set-locale LANG=#{language}") on(master, "service #{master['puppetservice']} restart") end def install_i18n_demo_module(node, environment=nil) env_options = environment.nil? ? '' : "--environment #{environment}" on(node, puppet("module install #{I18NDEMO_MODULE_NAME} #{env_options}")) end def uninstall_i18n_demo_module(node, environment=nil) env_options = environment.nil? ? '' : "--environment #{environment}" [I18NDEMO_MODULE_NAME, 'puppetlabs-stdlib', 'puppetlabs-translate'].each do |module_name| on(node, puppet("module uninstall #{module_name} #{env_options}"), :acceptable_exit_codes => [0,1]) end var_dir = on(node, puppet('config print vardir')).stdout.chomp on(node, "rm -rf '#{File.join(var_dir, 'locales', 'ja')}' '#{File.join(var_dir, 'locales', 'fi')}'") end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/module_utils.rb000066400000000000000000000260741470131746300270010ustar00rootroot00000000000000module Puppet module Acceptance module ModuleUtils # Return an array of module paths for a given host. # # Example return value: # # [ # "/etc/puppetlabs/code/environments/production/modules", # "/etc/puppetlabs/code/modules", # "/opt/puppet/share/puppet/modules", # ] # # @param host [String] hostname # @return [Array] paths for found modulepath def get_modulepaths_for_host(host) environment = on(host, puppet("config print environment")).stdout.chomp on(host, puppet("config print modulepath --environment #{environment}")).stdout.chomp.split(host['pathseparator']) end # Return a string of the default (first) path in modulepath for a given host. # # Example return value: # # "/etc/puppetlabs/code/environments/production/modules" # # @param host [String] hostname # @return [String] first path for found modulepath def get_default_modulepath_for_host(host) get_modulepaths_for_host(host)[0] end # Return an array of paths to installed modules for a given host. # # Example return value: # # [ # "/opt/puppet/share/puppet/modules/apt", # "/opt/puppet/share/puppet/modules/auth_conf", # "/opt/puppet/share/puppet/modules/concat", # ] # # @param host [String] hostname # @return [Array] paths for found modules def get_installed_modules_for_host(host) on(host, puppet('module list --render-as json')) do |result| str = result.stdout.lines.to_a.last pat = /\(([^()]+)\)/ mods = str.scan(pat).flatten return mods end end # Return a hash of array of paths to installed modules for a hosts. # The individual hostnames are the keys of the hash. The only value # for a given key is an array of paths for the found modules. # # Example return value: # # { # "my_master" => # [ # "/opt/puppet/share/puppet/modules/apt", # "/opt/puppet/share/puppet/modules/auth_conf", # "/opt/puppet/share/puppet/modules/concat", # ], # "my_agent01" => # [ # "/opt/puppet/share/puppet/modules/apt", # "/opt/puppet/share/puppet/modules/auth_conf", # "/opt/puppet/share/puppet/modules/concat", # ], # } # # @param hosts [Array] hostnames # @return [Hash] paths for found modules indexed by hostname def get_installed_modules_for_hosts(hosts) mods = {} hosts.each do |host| mods[host] = get_installed_modules_for_host host end return mods end # Compare the module paths in given hashes and remove paths that # are were not present in the first hash. The use case for this # method is to remove any modules that were installed during the # course of a test run. # # Installed module hashes would be gathered using the # `get_+installed_module_for_hosts` command in the setup stage # and teardown stages of a test. These hashes would be passed into # this method in order to find modules installed during the test # and delete them in order to return the SUT environments to their # initial state. # # TODO: Enhance to take versions into account, so that upgrade/ # downgrade events during a test does not persist in the SUT # environment. # # @param beginning_hash [Hash] paths for found modules indexed # by hostname. Taken in the setup stage of a test. # @param ending_hash [Hash] paths for found modules indexed # by hostname. Taken in the teardown stage of a test. def rm_installed_modules_from_hosts(beginning_hash, ending_hash) ending_hash.each do |host, mod_array| mod_array.each do |mod| if ! beginning_hash[host].include? mod on host, "rm -rf '#{mod}'" end end end end # Convert a semantic version number string to an integer. # # Example return value given an input of '1.2.42': # # 10242 # # @param semver [String] semantic version number def semver_to_i( semver ) # semver assumed to be in format .. # calculation assumes that each segment is < 100 tmp = semver.split('.') tmp[0].to_i * 10000 + tmp[1].to_i * 100 + tmp[2].to_i end # Compare two given semantic version numbers. # # Returns an integer indicating the relationship between the two: # 0 indicates that both are equal # a value greater than 0 indicates that the semver1 is greater than semver2 # a value less than 0 indicates that the semver1 is less than semver2 # def semver_cmp( semver1, semver2 ) semver_to_i(semver1) - semver_to_i(semver2) end # Assert that a module was installed according to the UI.. # # This is a wrapper to centralize the validation about how # the UI responded that a module was installed. # It is called after a call # to `on ( host )` and inspects # STDOUT for specific content. # # @param stdout [String] # @param module_author [String] the author portion of a module name # @param module_name [String] the name portion of a module name # @param module_verion [String] the version of the module to compare to # installed version # @param compare_op [String] the operator for comparing the verions of # the installed module def assert_module_installed_ui( stdout, module_author, module_name, module_version = nil, compare_op = nil ) valid_compare_ops = {'==' => 'equal to', '>' => 'greater than', '<' => 'less than'} assert_match(/#{module_author}-#{module_name}/, stdout, "Notice that module '#{module_author}-#{module_name}' was installed was not displayed") if version /#{module_author}-#{module_name} \(.*v(\d+\.\d+\.\d+)/ =~ stdout installed_version = Regexp.last_match[1] if valid_compare_ops.include? compare_op assert_equal( true, semver_cmp(installed_version, module_version).send(compare_op, 0), "Installed version '#{installed_version}' of '#{module_name}' was not #{valid_compare_ops[compare_op]} '#{module_version}'") end end end # Assert that a module is installed on disk. # # @param host [HOST] the host object to make the remote call on # @param module_name [String] the name portion of a module name # @param optional moduledir [String, Array] the path where the module should be, will # iterate over components of the modulepath by default. def assert_module_installed_on_disk(host, module_name, moduledir=nil) moduledir ||= get_modulepaths_for_host(host) modulepath = moduledir.is_a?(Array) ? moduledir : [moduledir] moduledir= nil modulepath.each do |i| # module directory should exist if on(host, %Q{[ -d "#{i}/#{module_name}" ]}, :acceptable_exit_codes => (0..255)).exit_code == 0 moduledir = i end end fail_test('module directory not found') unless moduledir owner = '' group = '' on host, %Q{ls -ld "#{moduledir}"} do listing = stdout.split(' ') owner = listing[2] group = listing[3] end # A module's files should have: # * a mode of 644 (755, if they're a directory) # * owner == owner of moduledir # * group == group of moduledir on host, %Q{ls -alR "#{moduledir}/#{module_name}"} do listings = stdout.split("\n") listings = listings.grep(/^[bcdlsp-]/) listings = listings.reject { |l| l =~ /\.\.$/ } listings.each do |line| fileinfo = parse_ls(line) assert_equal owner, fileinfo[:owner] assert_equal group, fileinfo[:group] if fileinfo[:filetype] == 'd' assert_equal 'rwx', fileinfo[:perms][:user] assert_equal 'r-x', fileinfo[:perms][:group] assert_equal 'r-x', fileinfo[:perms][:other] else assert_equal 'rw-', fileinfo[:perms][:user] assert_equal 'r--', fileinfo[:perms][:group] assert_equal 'r--', fileinfo[:perms][:other] end end end end LS_REGEX = %r[(.)(...)(...)(...).?[[:space:]]+\d+[[:space:]]+([[:word:]]+)[[:space:]]+([[:word:]]+).*[[:space:]]+([[:graph:]]+)$] def parse_ls(line) match = line.match(LS_REGEX) if match.nil? fail_test "#{line.inspect} doesn't match ls output regular expression" end captures = match.captures { :filetype => captures[0], :perms => { :user => captures[1], :group => captures[2], :other => captures[3], }, :owner => captures[4], :group => captures[5], :file => captures[6] } end private :parse_ls # Assert that a module is not installed on disk. # # @param host [HOST] the host object to make the remote call on # @param module_name [String] the name portion of a module name # @param optional moduledir [String, Array] the path where the module should be, will # iterate over components of the modulepath by default. def assert_module_not_installed_on_disk(host, module_name, moduledir=nil) moduledir ||= get_modulepaths_for_host(host) modulepath = moduledir.is_a?(Array) ? moduledir : [moduledir] moduledir= nil modulepath.each do |i| # module directory should not exist on host, %Q{[ ! -d "#{i}/#{module_name}" ]} end end # Create a simple directory environment and puppet.conf at :tmpdir. # # @note Also registers a teardown block to remove generated files. # # @param tmpdir [String] directory to contain all the # generated environment files # @return [String] path to the new puppet configuration file defining the # environments def generate_base_directory_environments(tmpdir) puppet_conf = "#{tmpdir}/puppet2.conf" dir_envs = "#{tmpdir}/environments" step 'Configure a the direnv directory environment' apply_manifest_on master, %Q{ File { ensure => directory, owner => #{master.puppet['user']}, group => #{master.puppet['group']}, mode => "0750", } file { [ '#{dir_envs}', '#{dir_envs}/direnv', ]: } file { '#{puppet_conf}': ensure => file, content => " [main] environmentpath=#{dir_envs} " } } return puppet_conf end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/puppet_type_test_tools.rb000066400000000000000000000100271470131746300311200ustar00rootroot00000000000000require 'puppet/acceptance/environment_utils' module Puppet module Acceptance module PuppetTypeTestTools include Puppet::Acceptance::EnvironmentUtils # for now, just for #random_string # FIXME: yardocs # TODO: create resource class which contains its manifest chunk, and assertions # can be an array or singular, holds the manifest and the assertion_code # has getter for the manifest # has #run_assertions(BeakerResult or string) def generate_manifest(test_resources) manifest = '' test_resources = [test_resources].flatten # ensure it's an array so we enumerate properly test_resources.each do |resource| manifest << resource[:pre_code] + "\n" if resource[:pre_code] namevar = (resource[:parameters][:namevar] if resource[:parameters]) || "#{resource[:type]}_#{random_string}" # ensure these are double quotes around the namevar incase users puppet-interpolate inside it # FIXME: add test ^^ manifest << resource[:type] + '{"' + namevar + '":' if resource[:type] if resource[:parameters] resource[:parameters].each do |key,value| next if key == :namevar manifest << "#{key} => #{value}," end end manifest << "}\n" if resource[:type] end return manifest end def generate_assertions(test_resources) assertion_code = '' test_resources = [test_resources].flatten # ensure it's an array so we enumerate properly test_resources.each do |resource| if resource[:assertions] resource[:assertions] = [resource[:assertions]].flatten # ensure it's an array so we enumerate properly resource[:assertions].each do |assertion_type| expect_failure = false if assertion_type[:expect_failure] expect_failure = true assertion_code << "expect_failure '#{assertion_type[:expect_failure][:message]}' do\n" # delete the message assertion_type[:expect_failure].delete(:message) # promote the hash in expect_failure assertion_type = assertion_type[:expect_failure] assertion_type.delete(:expect_failure) end # ensure all the values are arrays assertion_values = [assertion_type.values].flatten assertion_values.each do |assertion_value| # TODO: non matching asserts? # TODO: non stdout? (support stdout, stderr, exit_code) # TODO: what about checking resource state on host (non agent/apply #on use)? if assertion_type.keys.first =~ /assert_match/ assert_msg = 'found ' elsif assertion_type.keys.first =~ /refute_match/ assert_msg = 'did not find ' else assert_msg = '' end if assertion_value.is_a?(String) matcher = "\"#{assertion_value}\"" elsif assertion_value.is_a?(Regexp) matcher = assertion_value.inspect else matcher = assertion_value end assertion_code << "#{assertion_type.keys.first}(#{matcher}, result.stdout, '#{assert_msg}#{matcher}')\n" end assertion_code << "end\n" if expect_failure end end end return assertion_code end Result = Struct.new(:stdout) def run_assertions(assertions = '', result) result_struct = Result.new if result.respond_to? :stdout result_struct.stdout = result.stdout else # handle results sent in as string result_struct.stdout = result end result = result_struct begin eval(assertions) rescue RuntimeError, SyntaxError => e puts e puts assertions raise end end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/puppet_type_test_tools_spec.rb000066400000000000000000000147371470131746300321460ustar00rootroot00000000000000require File.join(File.dirname(__FILE__),'../../acceptance_spec_helper.rb') require 'puppet/acceptance/puppet_type_test_tools.rb' require 'beaker/dsl/assertions' require 'beaker/result' module Puppet module Acceptance describe 'PuppetTypeTestTools' do include PuppetTypeTestTools include Beaker::DSL::Assertions include Beaker context '#generate_manifest' do it 'takes a single hash' do expect(generate_manifest({:type => 'fake'})).to match(/^fake{"fake_\w{8}":}$/) end it 'takes an array' do expect(generate_manifest([{:type => 'fake'}])).to match(/^fake{"fake_\w{8}":}$/) end it 'generates empty puppet code (assertion-only instance)' do expect(generate_manifest({:fake => 'fake'})).to eql('') end it 'puts a namevar in the right place' do expect(generate_manifest({:type => 'fake', :parameters => {:namevar => 'blah'}})).to match(/^fake{"blah":}$/) end it 'retains puppet code in a namevar' do expect(generate_manifest({:type => 'fake', :parameters => {:namevar => "half_${interpolated}_puppet_namevar"}})). to match(/^fake{"half_\${interpolated}_puppet_namevar":}$/) end it 'places pre_code before the type' do expect(generate_manifest({:type => 'fake', :pre_code => '$some = puppet_code'})). to match(/^\$some = puppet_code\nfake{"fake_\w{8}":}$/m) end it 'places multiple, arbitrary parameters' do expect(generate_manifest({:type => 'fake', :parameters => {:someprop => "function(call)", :namevar => "blah", :someparam => 2}})). to match(/^fake{"blah":someprop => function\(call\),someparam => 2,}$/) end end context '#generate_assertions' do it 'takes a single hash' do expect(generate_assertions({:assertions => {:fake => 'matcher'}})) .to match(/^fake\("matcher", result\.stdout, '"matcher"'\)$/) end it 'takes an array' do expect(generate_assertions([{:assertions => {:fake => 'matcher'}}])) .to match(/^fake\("matcher", result\.stdout, '"matcher"'\)$/) end it 'generates empty assertions (puppet-code only instance)' do expect(generate_assertions({:type => 'no assertions'})).to eql('') end it 'generates arbitrary assertions' do expect(generate_assertions({:assertions => [{:fake => 'matcher'}, {:other => 'othermatch'}]})) .to match(/^fake\("matcher", result\.stdout, '"matcher"'\)\nother\("othermatch", result.stdout, '"othermatch"'\)$/m) end it 'can give a regex to assertions' do expect(generate_assertions({:assertions => {:fake => /matcher/}})) .to match(/^fake\(\/matcher\/, result\.stdout, '\/matcher\/'\)$/) end it 'allows multiple of one assertion type' do expect(generate_assertions({:assertions => {:fake => ['matcher','othermatch']}})) .to match(/^fake\("matcher", result\.stdout, '"matcher"'\)\nfake\("othermatch", result.stdout, '"othermatch"'\)$/) end it 'allows multiple assertion_types with multiple values' do expect(generate_assertions({:assertions => [{:fake => ['matcher','othermatch']}, {:fake2 => ['matcher2','othermatch2']}]})) .to match(/^fake\("matcher", result\.stdout, '"matcher"'\)\nfake\("othermatch", result.stdout, '"othermatch"'\)\nfake2\("matcher2", result.stdout, '"matcher2"'\)\nfake2\("othermatch2", result.stdout, '"othermatch2"'\)\n$/) end context 'expect_failure' do it 'generates arbitrary assertion' do expect(generate_assertions({:assertions => {:expect_failure => {:fake => 'matcher'}}})) .to match(/^expect_failure '' do\nfake\(.*\)\nend$/) end it 'allows multiple of one assertion type' do expect(generate_assertions({:assertions => {:expect_failure => {:fake => ['matcher','othermatch']}}})) .to match(/^expect_failure '' do\nfake\(.*\)\nfake\(.*\)\nend$/) end it 'allows multiple assertion_types' do pending 'ack! requires recursion :-(' #expect(generate_assertions({:assertions => {:expect_failure => [{:fake => 'matcher'},{:fake2 => 'matcher2'}]}})) #.to match(/^expect_failure '' do\nfake\(.*\)\nfake2\(.*\)\nend$/) end it 'allows multiple assertion_types with an expect_failure on one' do expect(generate_assertions({:assertions => [{:expect_failure => {:fake => 'matcher'}}, {:fake2 => 'matcher2'}]})) .to match(/^expect_failure '' do\nfake\(.*\)\nend\nfake2\(.*\)$/) end it 'allows custom expect_failure messages' do expect(generate_assertions({:assertions => {:expect_failure => {:fake => 'matcher', :message => 'oh noes, this should fail but pass'}}})) .to match(/^expect_failure 'oh noes, this should fail but pass' do\nfake\(.*\)\nend$/) end end it 'allow custom assertion messages' end context 'run_assertions' do #def run_assertions(assertions = '', result) it 'takes a string result' do expect(run_assertions('assert_match("yes please", result.stdout)', 'yes please')).to be true end let(:result) {Beaker::Result.new('host','command')} it 'takes a beaker "type" Result' do result.stdout = 'yes please' expect(run_assertions('assert_match("yes please", result.stdout)', result)).to be true end it 'runs a bunch of assertions' do result.stdout = 'yes please' expect(run_assertions("assert_match('yes please', result.stdout)\nrefute_match('blah', result.stdout)", result)).to be false end it 'fails assertions' do pending 'why doesnt this work?' result.stdout = 'yes please' expect(run_assertions('assert_match("blah", result.stdout)', result)).to raise_error end context 'exceptions' do #rescue RuntimeError, SyntaxError => e it 'puts the assertion code, raises error' do pending 'why doesnt this work?' expect(run_assertions('assert_match("blah") }', result)).to raise_error end end end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/rpm_util.rb000066400000000000000000000076641470131746300261330ustar00rootroot00000000000000module Puppet module Acceptance module RpmUtils # Utilities for creating a basic rpm package and using it in tests @@defaults = {:repo => '/tmp/rpmrepo', :pkg => 'mypkg', :publisher => 'tstpub.lan', :version => '1.0'} @@setup_packages = {} def rpm_provider(agent) has_dnf = on(agent, 'which dnf', :acceptable_exit_codes => [0,1]).exit_code if has_dnf == 0 'dnf' else 'yum' end end def setup(agent) @@setup_packages[agent] ||= {} cmd = rpm_provider(agent) required_packages = ['createrepo', 'curl', 'rpm-build'] required_packages.each do |pkg| pkg_installed = (on agent, "#{cmd} list installed #{pkg}", :acceptable_exit_codes => (0..255)).exit_code == 0 # package not present, so perform a new install if !pkg_installed on agent, "#{cmd} install -y #{pkg}" # package is present, but has not yet attempted an upgrade # note that this may influence YUM cache behavior elsif !@@setup_packages[agent].has_key?(pkg) # first pass, always attempt an upgrade to latest version # fixes Fedora 25 curl compat with python-pycurl for instance on agent, "#{cmd} upgrade -y #{pkg}" end @@setup_packages[agent][pkg] = true end end def clean_rpm(agent, o={}) cmd = rpm_provider(agent) o = @@defaults.merge(o) on agent, "rm -rf #{o[:repo]}", :acceptable_exit_codes => (0..255) on agent, "#{cmd} remove -y #{o[:pkg]}", :acceptable_exit_codes => (0..255) on agent, "rm -f /etc/yum.repos.d/#{o[:publisher]}.repo", :acceptable_exit_codes => (0..255) end def setup_rpm(agent, o={}) setup(agent) o = @@defaults.merge(o) on agent, "mkdir -p #{o[:repo]}/{RPMS,SRPMS,BUILD,SOURCES,SPECS}" on agent, "echo '%_topdir #{o[:repo]}' > ~/.rpmmacros" on agent, "createrepo #{o[:repo]}" on agent, "cat < /etc/yum.repos.d/#{o[:publisher]}.repo [#{o[:publisher]}] name=#{o[:publisher]} baseurl=file://#{o[:repo]}/ enabled=1 gpgcheck=0 EOF " end def send_rpm(agent, o={}) setup(agent) o = @@defaults.merge(o) on agent, "mkdir -p #{o[:repo]}/#{o[:pkg]}-#{o[:version]}/usr/bin" on agent, "cat < #{o[:repo]}/#{o[:pkg]} #!/bin/bash echo Hello World EOF " pkg_name = "#{o[:pkg]}-#{o[:version]}" on agent, "install -m 755 #{o[:repo]}/#{o[:pkg]} #{o[:repo]}/#{pkg_name}/usr/bin" on agent, "tar -zcvf #{o[:repo]}/SOURCES/#{pkg_name}.tar.gz -C #{o[:repo]} #{pkg_name}" on agent, "cat < #{o[:repo]}/SPECS/#{o[:pkg]}.spec # Don't try fancy stuff like debuginfo, which is useless on binary-only packages. Don't strip binary too # Be sure buildpolicy set to do nothing %define __spec_install_post %{nil} %define debug_package %{nil} %define __os_install_post %{_dbpath}/brp-compress Summary: A very simple toy bin rpm package Name: #{o[:pkg]} Version: #{o[:version]} Release: 1 Epoch: #{o[:epoch] || 0} BuildArch: noarch License: GPL+ Group: Development/Tools SOURCE0 : %{name}-%{version}.tar.gz URL: https://www.puppetlabs.com/ BuildRoot: %{_topdir}/BUILD/%{name}-%{version}-%{release}-root %description %{summary} %prep %setup -q %build # Empty section. %install rm -rf %{buildroot} mkdir -p %{buildroot} # in builddir cp -a * %{buildroot} %clean rm -rf %{buildroot} %files %defattr(-,root,root,-) %{_bindir}/* %changelog * Mon Dec 01 2014 Michael Smith #{o[:version]}-1 - First Build EOF " on agent, "rpmbuild -ba #{o[:repo]}/SPECS/#{o[:pkg]}.spec" on agent, "createrepo --update #{o[:repo]}" cmd = rpm_provider(agent) # DNF requires a cache reset to make local repositories accessible. if cmd == 'dnf' on agent, "dnf clean metadata" end end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/service_utils.rb000066400000000000000000000161441470131746300271510ustar00rootroot00000000000000require 'puppet/acceptance/common_utils' module Puppet module Acceptance module ServiceUtils # Return whether a host supports the systemd provider. # @param host [String] hostname # @return [Boolean] whether the systemd provider is supported. def supports_systemd?(host) # The Windows MSI doesn't put Puppet in the Ruby vendor or site dir, so loading it fails. return false if host.platform.variant == 'windows' ruby = Puppet::Acceptance::CommandUtils.ruby_command(host) suitable = on(host, "#{ruby} -e \"require 'puppet'; puts Puppet::Type.type(:service).provider(:systemd).suitable?\"" ).stdout.chomp suitable == "true" ? true : false end # Construct manifest ensuring service status. # @param service [String] name of the service # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys. # @return [String] a manifest def service_manifest(service, status) ensure_status = "ensure => '#{status[:ensure]}'," if status[:ensure] enable_status = "enable => '#{status[:enable]}'," if status[:enable] %Q{ service { '#{service}': #{ensure_status} #{enable_status} } } end # Alter the state of a service using puppet apply and assert that a change was logged. # Assumes the starting state is not the desired state. # @param host [String] hostname. # @param service [String] name of the service. # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys. # @return None def ensure_service_change_on_host(host, service, status) # the process of creating the service will also start it # to avoid a flickering test from the race condition, this test will ensure # that the exit code is either # 2 => something changed, or # 0 => no change needed apply_manifest_on(host, service_manifest(service, status), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/Service\[#{service}\]\/ensure: ensure changed '\w+' to '#{status[:ensure]}'/, result.stdout, 'Service status change failed') if status[:ensure] assert_match(/Service\[#{service}\]\/enable: enable changed '\w+' to '#{status[:enable]}'/, result.stdout, 'Service enable change failed') if status[:enable] end end # Ensure the state of a service using puppet apply and assert that no change was logged. # Assumes the starting state is the ensured state. # @param host [String] hostname. # @param service [String] name of the service. # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys. # @return None def ensure_service_idempotent_on_host(host, service, status) # ensure idempotency apply_manifest_on(host, service_manifest(service, status)) do |result| refute_match(/Service\[#{service}\]\/ensure/, result.stdout, 'Service status not idempotent') if status[:ensure] refute_match(/Service\[#{service}\]\/enable/, result.stdout, 'Service enable not idempotent') if status[:enable] end end # Alter the state of a service using puppet apply, assert that it changed and change is idempotent. # Can set 'ensure' and 'enable'. Assumes the starting state is not the desired state. # @param host [String] hostname. # @param service [String] name of the service. # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys. # @param block [Proc] optional: block to verify service state # @return None def ensure_service_on_host(host, service, status, &block) ensure_service_change_on_host(host, service, status) assert_service_status_on_host(host, service, status, &block) ensure_service_idempotent_on_host(host, service, status) assert_service_status_on_host(host, service, status, &block) end # Checks that the ensure and/or enable status of a service are as expected. # @param host [String] hostname. # @param service [String] name of the service. # @param status [Hash] properties to set - can include 'ensure' and 'enable' keys. # @param block [Proc] optional: block to verify service state # @return None def assert_service_status_on_host(host, service, status, &block) ensure_status = "ensure.+=> '#{status[:ensure]}'" if status[:ensure] enable_status = "enable.+=> '#{status[:enable]}'" if status[:enable] on(host, puppet_resource('service', service)) do |result| assert_match(/'#{service}'.+#{ensure_status}.+#{enable_status}/m, result.stdout, "Service status does not match expectation #{status}") end # Verify service state on the system using a custom block if block yield block end end # Refreshes a service. # @param host [String] hostname. # @param service [String] name of the service to refresh. # @return None def refresh_service_on_host(host, service) refresh_manifest = %Q{ service { '#{service}': } notify { 'Refreshing #{service}': notify => Service['#{service}'], } } apply_manifest_on(host, refresh_manifest) end # Runs some common acceptance tests for nonexistent services. # @param service [String] name of the service # @return None def run_nonexistent_service_tests(service) step "Verify that a nonexistent service is considered stopped, disabled and no logonaccount is reported" do on(agent, puppet_resource('service', service)) do |result| { enable: false, ensure: :stopped }.each do |property, value| assert_match(/#{property}.*#{value}.*$/, result.stdout, "Puppet does not report #{property}=#{value} for a non-existent service") end refute_match(/logonaccount\s+=>/, result.stdout, "Puppet reports logonaccount for a non-existent service") end end step "Verify that stopping and disabling a nonexistent service is a no-op" do manifest = service_manifest(service, ensure: :stopped, enable: false) apply_manifest_on(agent, manifest, catch_changes: true) end [ [ :enabling, [ :enable, true ]], [ :starting, [ :ensure, :running ]] ].each do |operation, (property, value)| manifest = service_manifest(service, property => value) step "Verify #{operation} a non-existent service prints an error message but does not fail the run without detailed exit codes" do apply_manifest_on(agent, manifest) do |result| assert_match(/Error:.*#{service}.*$/, result.stderr, "Puppet does not error when #{operation} a non-existent service.") end end step "Verify #{operation} a non-existent service with detailed exit codes correctly returns an error code" do apply_manifest_on(agent, manifest, :acceptable_exit_codes => [4]) end end end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/solaris_util.rb000066400000000000000000000133521470131746300270000ustar00rootroot00000000000000module Puppet module Acceptance module IPSUtils def clean(agent, o={}) o = {:repo => '/var/tstrepo', :pkg => 'mypkg', :publisher => 'tstpub.lan'}.merge(o) on agent, "rm -rf %s||:" % o[:repo] on agent, "rm -rf /tst||:" on agent, "pkg uninstall %s||:" % o[:pkg] on agent, "pkg unset-publisher %s ||:" % o[:publisher] end def setup(agent, o={}) o = {:repo => '/var/tstrepo', :publisher => 'tstpub.lan'}.merge(o) on agent, "mkdir -p %s" % o[:repo] on agent, "pkgrepo create %s" % o[:repo] on agent, "pkgrepo set -s %s publisher/prefix=%s" % [o[:repo], o[:publisher]] on agent, "pkgrepo -s %s refresh" % o[:repo] end def setup_fakeroot(agent, o={}) o = {:root=>'/opt/fakeroot'}.merge(o) on agent, "rm -rf %s" % o[:root] on agent, "mkdir -p %s/tst/usr/bin" % o[:root] on agent, "mkdir -p %s/tst/etc" % o[:root] on agent, "echo dummy > %s/tst/usr/bin/x" % o[:root] on agent, "echo val > %s/tst/etc/y" % o[:root] end def setup_fakeroot2(agent, o={}) o = {:root=>'/opt/fakeroot'}.merge(o) on agent, "rm -rf %s" % o[:root] on agent, "mkdir -p %s/tst2/usr/bin" % o[:root] on agent, "mkdir -p %s/tst2/etc" % o[:root] on agent, "echo dummy > %s/tst2/usr/bin/x" % o[:root] on agent, "echo val > %s/tst2/etc/y" % o[:root] end def send_pkg2(agent, o={}) o = {:repo=>'/var/tstrepo', :root=>'/opt/fakeroot', :publisher=>'tstpub.lan', :pkg=>'mypkg2@0.0.1', :pkgdep => 'mypkg@0.0.1'}.merge(o) on agent, "(pkgsend generate %s; echo set name=pkg.fmri value=pkg://%s/%s)> /tmp/%s.p5m" % [o[:root], o[:publisher], o[:pkg], o[:pkg]] on agent, "echo depend type=require fmri=%s >> /tmp/%s.p5m" % [o[:pkgdep], o[:pkg]] on agent, "pkgsend publish -d %s -s %s /tmp/%s.p5m" % [o[:root], o[:repo], o[:pkg]] on agent, "pkgrepo refresh -p %s -s %s" % [o[:publisher], o[:repo]] on agent, "pkg refresh" on agent, "pkg list -g %s" % o[:repo] end def send_pkg(agent, o={}) o = {:repo=>'/var/tstrepo', :root=>'/opt/fakeroot', :publisher=>'tstpub.lan', :pkg=>'mypkg@0.0.1'}.merge(o) on agent, "(pkgsend generate %s; echo set name=pkg.fmri value=pkg://%s/%s)> /tmp/%s.p5m" % [o[:root], o[:publisher], o[:pkg], o[:pkg]] on agent, "pkgsend publish -d %s -s %s /tmp/%s.p5m" % [o[:root], o[:repo], o[:pkg]] on agent, "pkgrepo refresh -p %s -s %s" % [o[:publisher], o[:repo]] on agent, "pkg refresh" end def set_publisher(agent, o={}) o = {:repo=>'/var/tstrepo', :publisher=>'tstpub.lan'}.merge(o) on agent, "pkg set-publisher -g %s %s" % [o[:repo], o[:publisher]] on agent, "pkg refresh" end end module SMFUtils def clean(agent, o={}) o = {:service => 'tstapp'}.merge(o) on(agent, "svcs -l %s" % o[:service], acceptable_exit_codes: [0, 1]) do |result| next if result.stdout =~ /doesn't match/ lines = result.stdout.chomp.lines instances = lines.select { |line| line =~ /^fmri/ }.map { |line| line.split(' ')[1].chomp } instances.each do |instance| on agent, "svcadm disable %s ||:" % instance on agent, "svccfg delete %s ||:" % instance end end on agent, "rm -rf /var/svc/manifest/application/%s.xml ||:" % o[:service] on agent, "rm -f /opt/bin/%s ||:" % o[:service] end def setup(agent, o={}) setup_methodscript(agent, o) end def setup_methodscript(agent, o={}) o = {:service => 'tstapp'}.merge(o) on agent, "mkdir -p /opt/bin" create_remote_file agent, '/lib/svc/method/%s' % o[:service], %[ #!/usr/bin/sh . /lib/svc/share/smf_include.sh case "$1" in start) /opt/bin/%s ;; stop) ctid=`svcprop -p restarter/contract $SMF_FMRI` if [ -n "$ctid" ]; then smf_kill_contract $ctid TERM fi ;; *) echo "Usage: $0 { start | stop }" ; exit 1 ;; esac exit $SMF_EXIT_OK ] % ([o[:service]] * 4) create_remote_file agent, ('/opt/bin/%s' % o[:service]), %[ #!/usr/bin/sh cleanup() { rm -f /tmp/%s.pidfile; exit 0 } trap cleanup INT TERM trap '' HUP (while :; do sleep 1; done) & echo $! > /tmp/%s.pidfile ] % ([o[:service]] * 2) on agent, "chmod 755 /lib/svc/method/%s" % o[:service] on agent, "chmod 755 /opt/bin/%s" % o[:service] on agent, "mkdir -p /var/svc/manifest/application" create_remote_file agent, ('/var/smf-%s.xml' % o[:service]), %[ ] % ([o[:service]] * 4) on agent, "svccfg -v validate /var/smf-%s.xml" % o[:service] on agent, "echo > /var/svc/log/application-%s:default.log" % o[:service] return ("/var/smf-%s.xml" % o[:service]), ("/lib/svc/method/%s" % o[:service]) end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/static_catalog_utils.rb000066400000000000000000000035371470131746300304740ustar00rootroot00000000000000module Puppet module Acceptance module StaticCatalogUtils # Adds code-id-command and code-content-command scripts # to the server and updates puppetserver.conf. This is # necessary for testing static catalogs. # @param master [String] the host running puppetserver. # @param scriptdir [String] the path to the directory where the scripts should be placed. def setup_puppetserver_code_id_scripts(master, scriptdir) code_id_command = < true) file { '#{scriptdir}/code_id.sh': ensure => file, content => "#{code_id_command}", mode => "0755", } file { '#{scriptdir}/code_content.sh': ensure => file, content => "#{code_content_command}", mode => "0755", } MANIFEST puppetserver_config = "#{master['puppetserver-confdir']}/puppetserver.conf" on master, "cp #{puppetserver_config} #{scriptdir}/puppetserver.conf.bak" versioned_code_settings = {"versioned-code" => {"code-id-command" => "#{scriptdir}/code_id.sh", "code-content-command" => "#{scriptdir}/code_content.sh"}} modify_tk_config(master, puppetserver_config, versioned_code_settings) end def cleanup_puppetserver_code_id_scripts(master, scriptdir) # These are -f so we don't bail on the teardown if for some reason they didn't get laid down on master, "rm -f #{scriptdir}/code_id.sh" on master, "rm -f #{scriptdir}/code_content.sh" puppetserver_config = "#{master['puppetserver-confdir']}/puppetserver.conf" on master, "cp #{scriptdir}/puppetserver.conf.bak #{puppetserver_config}" end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/temp_file_utils.rb000066400000000000000000000151311470131746300274500ustar00rootroot00000000000000module Puppet module Acceptance module TempFileUtils RWXR_XR_X = '0755' PUPPET_CODEDIR_PERMISSIONS = RWXR_XR_X # Return the name of the root user, as appropriate for the platform. def root_user(host) case host['platform'] when /windows/ 'Administrator' else 'root' end end # Return the name of the root group, as appropriate for the platform. def root_group(host) case host['platform'] when /windows/ 'Administrators' when /aix/ 'system' when /osx|bsd/ 'wheel' else 'root' end end # Create a file on the host. # Parameters: # [host] the host to create the file on # [file_path] the path to the file to be created # [file_content] a string containing the contents to be written to the file # [options] a hash containing additional behavior options. Currently supported: # * :mkdirs (default false) if true, attempt to create the parent directories on the remote host before writing # the file # * :owner (default 'root') the username of the user that the file should be owned by # * :group (default 'puppet') the name of the group that the file should be owned by # * :mode (default '644') the mode (file permissions) that the file should be created with def create_test_file(host, file_rel_path, file_content, options = {}) # set default options options[:mkdirs] ||= false options[:mode] ||= "755" unless options[:owner] if host['roles'].include?('master') then options[:owner] = host.puppet['user'] else options[:owner] = root_user(host) end end unless options[:group] if host['roles'].include?('master') then options[:group] = host.puppet['group'] else options[:group] = root_group(host) end end file_path = get_test_file_path(host, file_rel_path) mkdirs(host, File.dirname(file_path)) if (options[:mkdirs] == true) create_remote_file(host, file_path, file_content) # # NOTE: we need these chown/chmod calls because the acceptance framework connects to the nodes as "root", but # puppet 'master' runs as user 'puppet'. Therefore, in order for puppet master to be able to read any files # that we've created, we have to carefully set their permissions # chown(host, options[:owner], options[:group], file_path) chmod(host, options[:mode], file_path) end # Given a relative path, returns an absolute path for a test file. Basically, this just prepends the # a unique temp dir path (specific to the current test execution) to your relative path. def get_test_file_path(host, file_rel_path) initialize_temp_dirs unless @host_test_tmp_dirs File.join(@host_test_tmp_dirs[host.name], file_rel_path) end # Check for the existence of a temp file for the current test; basically, this just calls file_exists?(), # but prepends the path to the current test's temp dir onto the file_rel_path parameter. This allows # tests to be written using only a relative path to specify file locations, while still taking advantage # of automatic temp file cleanup at test completion. def test_file_exists?(host, file_rel_path) file_exists?(host, get_test_file_path(host, file_rel_path)) end def file_exists?(host, file_path) host.execute("test -f \"#{file_path}\"", :acceptable_exit_codes => [0, 1]) do |result| return result.exit_code == 0 end end def dir_exists?(host, dir_path) host.execute("test -d \"#{dir_path}\"", :acceptable_exit_codes => [0, 1]) do |result| return result.exit_code == 0 end end def link_exists?(host, link_path) host.execute("test -L \"#{link_path}\"", :acceptable_exit_codes => [0, 1]) do |result| return result.exit_code == 0 end end def file_contents(host, file_path) host.execute("cat \"#{file_path}\"") do |result| return result.stdout end end def tmpdir(host, basename) host_tmpdir = host.tmpdir(basename) # we need to make sure that the puppet user can traverse this directory... chmod(host, "755", host_tmpdir) host_tmpdir end def mkdirs(host, dir_path) on(host, "mkdir -p #{dir_path}") end def chown(host, owner, group, path) on(host, "chown #{owner}:#{group} #{path}") end def chmod(host, mode, path) on(host, "chmod #{mode} #{path}") end # Returns an array containing the owner, group and mode of # the file specified by path. The returned mode is an integer # value containing only the file mode, excluding the type, e.g # S_IFDIR 0040000 def stat(host, path) require File.join(File.dirname(__FILE__),'common_utils.rb') ruby = Puppet::Acceptance::CommandUtils.ruby_command(host) owner = on(host, "#{ruby} -e 'require \"etc\"; puts (Etc.getpwuid(File.stat(\"#{path}\").uid).name)'").stdout.chomp group = on(host, "#{ruby} -e 'require \"etc\"; puts (Etc.getgrgid(File.stat(\"#{path}\").gid).name)'").stdout.chomp mode = on(host, "#{ruby} -e 'puts (File.stat(\"#{path}\").mode & 07777)'").stdout.chomp.to_i [owner, group, mode] end def initialize_temp_dirs() # pluck this out of the test case environment; not sure if there is a better way @cur_test_file = @path @cur_test_file_shortname = File.basename(@cur_test_file, File.extname(@cur_test_file)) # we need one list of all of the hosts, to assist in managing temp dirs. It's possible # that the master is also an agent, so this will consolidate them into a unique set @all_hosts = Set[master, *agents] # now we can create a hash of temp dirs--one per host, and unique to this test--without worrying about # doing it twice on any individual host @host_test_tmp_dirs = Hash[@all_hosts.map do |host| [host.name, tmpdir(host, @cur_test_file_shortname)] end ] end def remove_temp_dirs() @all_hosts.each do |host| on(host, "rm -rf #{@host_test_tmp_dirs[host.name]}") end end # a silly variable for keeping track of whether or not all of the tests passed... @all_tests_passed = false end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/windows_utils.rb000066400000000000000000000034711470131746300272020ustar00rootroot00000000000000require 'puppet/acceptance/common_utils' module Puppet module Acceptance module WindowsUtils require 'puppet/acceptance/windows_utils/service.rb' require 'puppet/acceptance/windows_utils/package_installer.rb' def profile_base(agent) ruby = Puppet::Acceptance::CommandUtils.ruby_command(agent) getbasedir = <<'END' puts ENV['USERPROFILE'].match(/(.*)\\\\[^\\\\]*/)[1] END on(agent, "#{ruby} -e \"#{getbasedir}\"").stdout.chomp end # Checks whether the account with the given username has the given password on a host def assert_password_matches_on(host, username, password, msg = nil) script = <<-PS1 Add-Type -AssemblyName System.DirectoryServices.AccountManagement $ctx = New-Object System.DirectoryServices.AccountManagement.PrincipalContext([System.DirectoryServices.AccountManagement.ContextType]::Machine, $env:COMPUTERNAME) $ctx.ValidateCredentials("#{username}", "#{password}") PS1 result = execute_powershell_script_on(host, script) assert_match(/True/, result.stdout.strip, msg) end def deny_administrator_access_to(host, filepath) # we need to create a fake directory in the user's tempdir with powershell because the ACL # perms set down by cygwin when making tempdirs makes the ACL unusable. Thus we create a # tempdir using powershell and pull its' ACL as a starting point for the new ACL. script = <<-PS1 mkdir -Force $env:TMP\\fake-dir-for-acl $acl = Get-ACL $env:TMP\\fake-dir-for-acl rm -Force $env:TMP\\fake-dir-for-acl $ar = New-Object system.security.accesscontrol.filesystemaccessrule("Administrator","FullControl","Deny") $acl.SetAccessRule($ar) Set-ACL #{filepath} $acl PS1 execute_powershell_script_on(host, script) end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/windows_utils/000077500000000000000000000000001470131746300266505ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/windows_utils/package_installer.rb000066400000000000000000000056471470131746300326610ustar00rootroot00000000000000module Puppet module Acceptance module WindowsUtils # Sets up a mock installer on the host. def create_mock_package(host, tmpdir, config = {}, installer_file = 'MockInstaller.cs', uninstaller_file = 'MockUninstaller.cs') installer_exe_path = "#{tmpdir}/#{config[:name].gsub(/\s+/, '')}Installer.exe".gsub('/', '\\') uninstaller_exe_path = "#{tmpdir}/#{config[:name].gsub(/\s+/, '')}Uninstaller.exe".gsub('/', '\\') tranformations = { package_display_name: config[:name], uninstaller_location: uninstaller_exe_path, install_commands: config[:install_commands], uninstall_commands: config[:uninstall_commands] } [ { source: installer_file, destination: installer_exe_path }, { source: uninstaller_file, destination: uninstaller_exe_path }, ].each do |exe| fixture_path = File.join( File.dirname(__FILE__), '..', '..', '..', '..', 'fixtures', exe[:source] ) code = File.read(fixture_path) % tranformations build_mock_exe(host, exe[:destination], code) end # If the registry key still exists from a previous package install, then delete it. teardown do if package_installed?(host, config[:name]) on host, powershell("\"Remove-Item HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\#{config[:name]}\"") end end # return the installer path for tests to use as the source: attribute installer_exe_path end def build_mock_exe(host, destination, code) # Make a source file containing the code on the SUT, the source file # will be the same location/name as the destination exe but with the .cs # extension source_path_on_host = destination.gsub(/\.exe$/, '.cs') create_remote_file(host, source_path_on_host.gsub('\\', '/'), code) # Create the installer.exe file by compiling the copied over C# code # with PowerShell create_installer_exe = "\"Add-Type"\ " -TypeDefinition (Get-Content #{source_path_on_host} | Out-String)"\ " -Language CSharp"\ " -OutputAssembly #{destination}"\ " -OutputType ConsoleApplication\"" on host, powershell(create_installer_exe) end def package_installed?(host, name) # A successfully installed mock package will have created a registry key under # HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall. Simply checking # for that key should suffice as an indicator that the installer completed test_key = "\"Test-Path HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\#{name}\"" on(host, powershell(test_key)) do |result| return result.stdout.chomp == 'True' end end end end end puppetlabs-puppet-789f600/acceptance/lib/puppet/acceptance/windows_utils/service.rb000066400000000000000000000112471470131746300306420ustar00rootroot00000000000000module Puppet module Acceptance module WindowsUtils # Sets up a mock service on the host. The methodology here is a simplified # version of what's described in https://msdn.microsoft.com/en-us/magazine/mt703436.aspx def setup_service(host, config = {}, service_file = 'MockService.cs') config[:name] ||= "Mock Service" config[:display_name] ||= "#{config[:name]} (Puppet Acceptance Tests)" config[:description] ||= "Service created solely for acceptance testing the Puppet Windows Service provider" # Create a temporary directory to store the service's C# source code + # its .exe file. tmpdir = host.tmpdir("mock_service") # Copy-over the C# code code_fixture_path = File.join( File.dirname(__FILE__), '..', '..', '..', '..', 'fixtures', service_file ) code = File.read(code_fixture_path) % { service_name: config[:name], start_sleep: config[:start_sleep], pause_sleep: config[:pause_sleep], continue_sleep: config[:continue_sleep], stop_sleep: config[:stop_sleep] } code_path_unix = "#{tmpdir}/source.cs" code_path_win = code_path_unix.gsub('/', '\\') create_remote_file(host, code_path_unix, code) # Create the service.exe file by compiling the copied over C# code # with PowerShell service_exe_path_win = "#{tmpdir}/#{config[:name]}.exe".gsub('/', '\\') create_service_exe = "\"Add-Type"\ " -TypeDefinition (Get-Content #{code_path_win} | Out-String)"\ " -Language CSharp"\ " -OutputAssembly #{service_exe_path_win}"\ " -OutputType ConsoleApplication"\ " -ReferencedAssemblies 'System.ServiceProcess'\"" on host, powershell(create_service_exe) # Now register the service with SCM register_service_with_scm = "\"New-Service"\ " #{config[:name]}"\ " #{service_exe_path_win}"\ " -DisplayName '#{config[:display_name]}'"\ " -Description '#{config[:description]}'"\ " -StartupType Automatic\"" on host, powershell(register_service_with_scm) # Ensure that our service is deleted after the tests teardown { delete_service(host, config[:name]) } end def delete_service(host, name) # Check if our service has already been deleted. If so, then we # have nothing else to do. begin on host, powershell("Get-Service #{name}") rescue Beaker::Host::CommandFailure return end # Ensure that our service process is killed. We cannot do a Stop-Service here # b/c there's a chance that our service could be in a pending state (e.g. # "PausePending", "ContinuePending"). If this is the case, then Stop-Service # will fail. on host, powershell("\"Get-Process #{name} -ErrorAction SilentlyContinue | Stop-Process -Force\" | exit 0") # Now remove our service. We use sc.exe because older versions of PowerShell # may not have the Remove-Service commandlet. on agent, "sc.exe delete #{name}" end # Config should be a hash of => def assert_service_properties_on(host, name, properties = {}) properties.each do |property, expected_value| # We need to get the underlying WMI object for the service since that # object contains all of our service properties. The one returned by # Get-Service only has these properties for newer versions of PowerShell. get_property_value = "\"Get-WmiObject -Class Win32_Service"\ " | Where-Object { \\$_.name -eq '#{name}' }"\ " | ForEach-Object { \\$_.#{property} }\"" on(host, powershell(get_property_value)) do |result| actual_value = result.stdout.chomp property_str = "#{name}[#{property}]" assert_match(expected_value, actual_value, "EXPECTED: #{property_str} = #{expected_value}, ACTUAL: #{property_str} = #{actual_value}") end end end def assert_service_startmode_delayed(host, name) get_delayed_service = "\"Get-ChildItem HKLM:\\SYSTEM\\CurrentControlSet\\Services"\ " | Where-Object { \\$_.Property -Contains 'DelayedAutoStart' -And \\$_.PsChildName -Like '#{name}' }"\ " | Select-Object -ExpandProperty PSChildName\"" on(host, powershell(get_delayed_service)) do |result| svc = result.stdout.chomp assert(!svc.empty?, "Service #{name} does not exist or is not a delayed service") end end end end end puppetlabs-puppet-789f600/acceptance/pending/000077500000000000000000000000001470131746300212115ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/pending/ticket_11860_exec_should_not_override_locale.rb000066400000000000000000000055501470131746300322450ustar00rootroot00000000000000test_name "#11860: exec resources should not override system locale" ####################################################################################### # NOTE ####################################################################################### # # This test won't run properly until the test agent nodes have the Spanish language # pack installed on them. On an ubuntu system, this can be done with the following: # # apt-get install language-pack-es-base # # Also, this test depends on the following pull requests: # # https://github.com/puppetlabs/puppet-acceptance/pull/123 # https://github.com/puppetlabs/facter/pull/159 # ####################################################################################### temp_file_name = "/tmp/11860_exec_should_not_override_locale.txt" locale_string = "es_ES.UTF-8" step "Check value of LANG environment variable" # in this step we are going to run an "exec" block that writes the value of the LANG # environment variable to a file. We need to verify that exec's are no longer # forcefully setting this var to 'C'. test_LANG_manifest = < "/usr/bin/printenv LANG > #{temp_file_name}", } HERE # apply the manifest. # # note that we are passing in an extra :environment argument, which will cause the # framework to temporarily set this variable before executing the puppet command. # this lets us know what value we should be looking for as the output of the exec. apply_manifest_on agents, test_LANG_manifest, :environment => {:LANG => locale_string} # cat the temp file and make sure it contained the correct value. on(agents, "cat #{temp_file_name}").each do |result| assert_equal(locale_string, "#{result.stdout.chomp}", "Unexpected result for host '#{result.host}'") end step "Check for locale-specific output of cat command" # in this step we are going to run an "exec" block that runs the "cat" command. The command # is intentionally invalid, because we are going to run it using a non-standard locale and # we want to confirm that the error message is in the correct language. test_cat_manifest = < "/bin/cat SOME_FILE_THAT_DOESNT_EXIST > #{temp_file_name} 2>&1", returns => 1, } HERE # apply the manifest, again passing in the extra :environment argument to set our locale. apply_manifest_on agents, test_cat_manifest, :environment => {:LANG => locale_string} # cat the output file and ensure that the error message is in spanish on(agents, "cat #{temp_file_name}").each do |result| assert_equal("/bin/cat: SOME_FILE_THAT_DOESNT_EXIST: No existe el fichero o el directorio", "#{result.stdout.chomp}", "Unexpected result for host '#{result.host}'") end step "cleanup" # remove the temp file on agents, "rm -f #{temp_file_name}" puppetlabs-puppet-789f600/acceptance/pending/ticket_4149_parseonly_should_not_fail.rb000066400000000000000000000016011470131746300310250ustar00rootroot00000000000000test_name "#4149: parseonly should do the right thing" step "test with a manifest with syntax errors" manifest = 'class someclass { notify { "hello, world" } }' apply_manifest_on(agents, manifest, :parseonly => true, :acceptable_exit_codes => [1]) { stdout =~ /Could not parse for .*: Syntax error/ or fail_test("didn't get a reported systax error") } step "test with a manifest with correct syntax" apply_manifest_on agents,'class someclass { notify("hello, world") }', :parseonly => true # REVISIT: This tests the current behaviour, which is IMO not actually the # correct behaviour. On the other hand, if we change this we might # unexpectedly break things out in the wild, so better to be warned than to be # surprised by it. --daniel 2010-12-22 step "test with a class with an invalid attribute" apply_manifest_on agents, 'file { "/tmp/whatever": fooble => 1 }', :parseonly => true ticket_4151_defined_function_should_not_return_true_for_unrealized_virtual_resources.rb000066400000000000000000000014041470131746300426220ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/pendingtest_name "#4151: defined function should not return true for unrealized virtual resources" # Jeff McCune # 2010-07-06 # # This script is expected to exit non-zero if ticket 4151 has not been # fixed. # # The expected behavior is for defined() to only return true if a virtual # resource has been realized. # # This test creates a virtual resource, does NOT realize it, then calls # the defined() function against it. If defined returns true, there will # be an error since Notify["goodbye"] will require a resource which has # not been realized. manifest1 = %q{ @notify { "hello": } if (defined(Notify["hello"])) { $requires = [ Notify["hello"] ] } notify { "goodbye": require => $requires } } apply_manifest_on(agents, manifest1) puppetlabs-puppet-789f600/acceptance/pending/ticket_5027_warn_on_dynamic_scope.rb000066400000000000000000000014131470131746300301150ustar00rootroot00000000000000test_name "#5027: Issue warnings when using dynamic scope" step "Apply dynamic scoping manifest on agents" apply_manifest_on agents, %q{ $foo = 'foo_value' class a { $bar = 'bar_value' include b } class b inherits c { notify { $baz: } # should not generate a warning -- inherited from class c notify { $bar: } # should generate a warning -- uses dynamic scoping notify { $foo: } # should not generate a warning -- comes from top scope } class c { $baz = 'baz_value' } include a } step "Verify deprecation warning" fail_test "Deprecation warning not issued" unless stdout.include? 'warning: Dynamic lookup of $bar will not be supported in future versions. Use a fully-qualified variable name or parameterized classes.' puppetlabs-puppet-789f600/acceptance/pending/ticket_5224_exec_should_unset_user_env_vars.rb000066400000000000000000000073521470131746300322450ustar00rootroot00000000000000test_name "#5224: exec resources should unset user-related environment variables" ####################################################################################### # NOTE ####################################################################################### # # This test depends on the following pull requests: # # https://github.com/puppetlabs/puppet-acceptance/pull/123 # # because it needs to be able to set some environment variables for the duration of # the puppet commands. Shouldn't be moved out of 'pending' until after that has been # merged. # ####################################################################################### temp_file_name = "/tmp/5224_exec_should_unset_user_env_vars.txt" sentinel_string = "Abracadabra" # these should match up with the value of Puppet::Util::POSIX_USER_ENV_VARS, # but I don't have access to that from here, so this is unfortunately hard-coded # (cprice 2012-01-27) POSIX_USER_ENV_VARS = ['HOME', 'USER', 'LOGNAME'] step "Check value of user-related environment variables" # in this step we are going to run some "exec" blocks that writes the value of the # user-related environment variables to a file. We need to verify that exec's are # unsetting these vars. test_printenv_manifest = < "/usr/bin/printenv %s > #{temp_file_name}", } HERE # loop over the vars that we care about; these should match up with the value of Puppet::Util::POSIX_USER_ENV_VARS, # but I don't have access to that from here, so this is unfortunately hard-coded (cprice 2012-01-27) POSIX_USER_ENV_VARS.each do |var| # apply the manifest. # # note that we are passing in an extra :environment argument, which will cause the # framework to temporarily set this variable before executing the puppet command. # this lets us know what value we should be looking for as the output of the exec. apply_manifest_on agents, test_printenv_manifest % [var, var], :environment => {var => sentinel_string} # cat the temp file and make sure it contained the correct value. on(agents, "cat #{temp_file_name}").each do |result| assert_equal("", "#{result.stdout.chomp}", "Unexpected result for host '#{result.host}', environment var '#{var}'") end end step "Check value of user-related environment variables when they are provided as part of the exec resource" # in this step we are going to run some "exec" blocks that write the value of the # user-related environment variables to a file. However, this time, the manifest # explicitly overrides these variables in the "environment" section, so we need to # be sure that we are respecting these overrides. test_printenv_with_env_overrides_manifest = < "/usr/bin/printenv %s > #{temp_file_name}", environment => ["%s=#{sentinel_string}", "FOO=bar"] } HERE # loop over the vars that we care about; POSIX_USER_ENV_VARS.each do |var| # apply the manifest. # # note that we are passing in an extra :environment argument, which will cause the # framework to temporarily set this variable before executing the puppet command. # this lets us know what value we should be looking for as the output of the exec. apply_manifest_on agents, test_printenv_with_env_overrides_manifest % [var, var, var], :environment => {var => sentinel_string} # cat the temp file and make sure it contained the correct value. on(agents, "cat #{temp_file_name}").each do |result| assert_equal(sentinel_string, "#{result.stdout.chomp}", "Unexpected result for host '#{result.host}', environment var '#{var}'") end end step "cleanup" # remove the temp file on agents, "rm -f #{temp_file_name}" puppetlabs-puppet-789f600/acceptance/pending/ticket_6928_puppet_master_parse_fails.rb000066400000000000000000000033411470131746300310320ustar00rootroot00000000000000test_name "#6928: Puppet --parseonly should return deprication message" # Create good and bad formatted manifests step "Master: create valid, invalid formatted manifests" create_remote_file(master, '/tmp/good.pp', %w{notify{good:}} ) create_remote_file(master, '/tmp/bad.pp', 'notify{bad:') step "Master: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning" on master, puppet_master( %w{--parseonly /tmp/bad.pp} ), :acceptable_exit_codes => [ 1 ] fail_test "Deprecation warning not issued for --parseonly" unless stdout.include? '--parseonly has been removed. Please use \'puppet parser validate \'' step "Agents: create valid, invalid formatted manifests" agents.each do |host| create_remote_file(host, '/tmp/good.pp', %w{notify{good:}} ) create_remote_file(host, '/tmp/bad.pp', 'notify{bad:') end step "Agents: use --parseonly on an invalid manifest, should return 1 and issue deprecation warning" agents.each do |host| on(host, "puppet --parseonly /tmp/bad.pp}", :acceptable_exit_codes => [ 1 ]) do fail_test "Deprecation warning not issued for --parseonly" unless stdout.include? '--parseonly has been removed. Please use \'puppet parser validate \'' end end step "Test Face for ‘parser validate’ with good manifest -- should pass" agents.each do |host| on(host, "puppet parser validate /tmp/good.pp", :acceptable_exit_codes => [ 0 ]) end step "Test Face for ‘parser validate’ with bad manifest -- should fail" agents.each do |host| on(host, "puppet parser validate /tmp/bad.pp", :acceptable_exit_codes => [ 1 ]) do fail_test "Bad manifest detection failed" unless stderr.include? 'Could not run: Could not parse for environment production' end end puppetlabs-puppet-789f600/acceptance/teardown/000077500000000000000000000000001470131746300214105ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/teardown/common/000077500000000000000000000000001470131746300227005ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/teardown/common/099_Archive_Logs.rb000066400000000000000000000114101470131746300261700ustar00rootroot00000000000000require 'date' def file_glob(host, path) result = on(host, "ls #{path}", :acceptable_exit_codes => [0, 2]) return [] if result.exit_code != 0 return result.stdout.strip.split("\n") end # This test is prefixed with zzz so it will hopefully run last. test_name 'Backup puppet logs and app data on all hosts' do today = Date.today().to_s # truncate the job name so it only has the name-y part and no parameters job_name = (ENV['JOB_NAME'] || 'unknown_jenkins_job') .sub(/[A-Z0-9_]+=.*$/, '') .gsub(/[\/,.]/, '_')[0..200] archive_name = "#{job_name}__#{ENV['BUILD_ID']}__#{today}__sut-files.tgz" archive_root = "SUT_#{today}" hosts.each do |host| step("Capturing log errors for #{host}") do case host[:platform] when /windows/ # on Windows, all of the desired data (including logs) is in the data dir puppetlabs_data = 'C:/ProgramData/PuppetLabs' archive_file_from(host, puppetlabs_data, {}, archive_root, archive_name) # Note: Windows `ls` uses absolute paths for all matches when an absolute path is supplied. tempdir = 'C:/Windows/TEMP' file_glob(host, File.join(tempdir, 'install-puppet-*.log')).each do |install_log| archive_file_from(host, install_log, {}, archive_root, archive_name) end file_glob(host, File.join(tempdir, 'puppet-*-installer.log')).each do |install_log| archive_file_from(host, install_log, {}, archive_root, archive_name) end else puppetlabs_logdir = '/var/log/puppetlabs' grep_for_alerts = if host[:platform] =~ /solaris/ "egrep -i 'warn|error|fatal'" elsif host[:platform] =~ /aix/ "grep -iE -B5 -A10 'warn|error|fatal'" else "grep -i -B5 -A10 'warn\\|error\\|fatal'" end ## If there are any PL logs, try to echo all warning, error, and fatal ## messages from all PL logs to the job's output on(host, <<-GREP_FOR_ALERTS, :accept_all_exit_codes => true ) if [ -d #{puppetlabs_logdir} ] && [ -n "$(find #{puppetlabs_logdir} -name '*.log*')" ]; then for log in $(find #{puppetlabs_logdir} -name '*.log*'); do # grep /dev/null only to get grep to print filenames, since -H is not in POSIX spec for grep #{grep_for_alerts} $log /dev/null; echo "" done fi GREP_FOR_ALERTS step("Archiving logs for #{host} into #{archive_name} (muzzling everything but :warn or higher beaker logs...)") do ## turn the logger off to avoid getting hundreds of lines of scp progress output previous_level = @logger.log_level @logger.log_level = :warn pxp_cache = '/opt/puppetlabs/pxp-agent/spool' puppetlabs_data = '/etc/puppetlabs' version_lookup_result = on(host, "cat /opt/puppetlabs/puppet/VERSION", :accept_all_exit_codes => true) # If we can't find a VERSION file, chances are puppet wasn't # installed and these paths aren't present. Beaker's # archive_file_from() will fail if it can't find the file, and we # want to proceed... if version_lookup_result.exit_code == 0 agent_version = version_lookup_result.output.strip archive_file_from(host, pxp_cache, {}, archive_root, archive_name) unless version_is_less(agent_version, "1.3.2") archive_file_from(host, puppetlabs_data, {}, archive_root, archive_name) archive_file_from(host, puppetlabs_logdir, {}, archive_root, archive_name) end syslog_dir = '/var/log' syslog_name = 'messages' if host[:platform] =~ /ubuntu|debian/ syslog_name = 'syslog' elsif host[:platform] =~ /solaris/ syslog_dir = '/var/adm' # Next few lines are for debugging POOLER-200, once that is resolved this can be removed @logger.log_level = previous_level on(host, 'egrep -i \'reboot after panic\' /var/adm/messages', :acceptable_exit_codes => [0,1,2]) @logger.log_level = :warn elsif host[:platform] =~ /osx/ syslog_name = "system.log" elsif host[:platform] =~ /fedora/ on(host, "journalctl --no-pager > /var/log/messages") elsif host[:platform] =~ /aix/ on(host, "alog -o -t console > /var/log/messages") end syslog_path = File.join(syslog_dir, syslog_name) if host.file_exist?(syslog_path) archive_file_from(host, syslog_path, {}, archive_root, archive_name) end ## turn the logger back on in case someone else wants to log things @logger.log_level = previous_level end end end end end puppetlabs-puppet-789f600/acceptance/tests/000077500000000000000000000000001470131746300207275ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/agent/000077500000000000000000000000001470131746300220255ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/agent/agent_disable_lockfile.rb000066400000000000000000000074631470131746300270150ustar00rootroot00000000000000test_name "C4553 - agent --disable/--enable functionality should manage the agent lockfile properly" tag 'audit:integration', # lockfile uses the standard `vardir` location to store/query lockfile. # The validation of the `vardir` at the OS level # should be accomplished in another test. 'audit:high', 'audit:refactor' # This test should not require a master. Remove the use of `with_puppet_running_on`. # # This test is intended to ensure that puppet agent --enable/--disable # work properly, both in terms of complying with our public "API" around # lockfile semantics ( http://links.puppet.com/agent_lockfiles ), and # in terms of actually restricting or allowing new agent runs to begin. # require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() @all_tests_passed = false ############################################################################### # BEGIN TEST LOGIC ############################################################################### teardown do if @all_tests_passed then remove_temp_dirs() end agents.each do |agent| on(agent, puppet('agent', "--enable")) end end tuples = [ ["reason not specified", false], ["I'm busy; go away.'", true] ] with_puppet_running_on(master, {}) do tuples.each do |expected_message, explicitly_specify_message| step "disable the agent; specify message? '#{explicitly_specify_message}', message: '#{expected_message}'" do agents.each do |agent| if (explicitly_specify_message) on(agent, puppet('agent', "--disable \"#{expected_message}\"")) else on(agent, puppet('agent', "--disable")) end agent_disabled_lockfile = "#{agent.puppet['vardir']}/state/agent_disabled.lock" unless file_exists?(agent, agent_disabled_lockfile) then fail_test("Failed to create disabled lock file '#{agent_disabled_lockfile}' on agent '#{agent}'") end lock_file_content = file_contents(agent, agent_disabled_lockfile) # This is a hack; we should parse the JSON into a hash, but I don't # think I have a library available from the acceptance test framework # that I can use to do that. So I'm falling back to regex. lock_file_content_regex = /"disabled_message"\s*:\s*"#{expected_message}"/ unless lock_file_content =~ lock_file_content_regex fail_test("Disabled lock file contents invalid; expected to match '#{lock_file_content_regex}', got '#{lock_file_content}' on agent '#{agent}'") end end end step "attempt to run the agent (message: '#{expected_message}')" do agents.each do |agent| on(agent, puppet('agent', "--test"), :acceptable_exit_codes => [1]) do |result| disabled_regex = /administratively disabled.*'#{expected_message}'/ unless result.stdout =~ disabled_regex fail_test("Unexpected output from attempt to run agent disabled; expecting to match '#{disabled_regex}', got '#{result.stdout}' on agent '#{agent}'") unless agent['locale'] == 'ja' end end end end step "enable the agent (message: '#{expected_message}')" do agents.each do |agent| agent_disabled_lockfile = "#{agent.puppet['vardir']}/state/agent_disabled.lock" on(agent, puppet('agent', "--enable")) if file_exists?(agent, agent_disabled_lockfile) then fail_test("Failed to remove disabled lock file '#{agent_disabled_lockfile}' on agent '#{agent}'") end end end step "verify that we can run the agent (message: '#{expected_message}')" do agents.each do |agent| on(agent, puppet('agent', "--test")) end end end # tuples block end # with_puppet_running_on block @all_tests_passed = true puppetlabs-puppet-789f600/acceptance/tests/agent/agent_fails_with_unknown_resource.rb000066400000000000000000000051311470131746300313470ustar00rootroot00000000000000test_name "agent run should fail if it finds an unknown resource type" do tag 'audit:high', 'audit:integration' require 'puppet/acceptance/common_utils' require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils step "agent should fail when it can't find a resource" do vendor_modules_path = master.tmpdir('vendor_modules') tmp_environment = mk_tmp_environment_with_teardown(master, 'tmp') site_pp_content = <<-SITEPP define foocreateresource($one) { $msg = 'hello' notify { $name: message => $msg } } class example($x) { if $x == undef or $x == [] or $x == '' { notice 'foo' return() } notice 'bar' } node default { class { example: x => [] } create_resources('foocreateresource', {'blah'=>{'one'=>'two'}}) mycustomtype{'foobar':} } SITEPP manifests_path = "/tmp/#{tmp_environment}/manifests" on(master, "mkdir -p '#{manifests_path}'") create_remote_file(master, "#{manifests_path}/site.pp", site_pp_content) custom_type_content = <<-CUSTOMTYPE Puppet::Type.newtype(:mycustomtype) do @doc = "Create a new mycustomtype thing." newparam(:name, :namevar => true) do desc "Name of mycustomtype instance" end def refresh end end CUSTOMTYPE type_path = "#{vendor_modules_path}/foo/lib/puppet/type" on(master, "mkdir -p '#{type_path}'") create_remote_file(master, "#{type_path}/mycustomtype.rb", custom_type_content) on(master, "chmod -R 750 '#{vendor_modules_path}' '/tmp/#{tmp_environment}'") on(master, "chown -R #{master.puppet['user']}:#{master.puppet['group']} '#{vendor_modules_path}' '/tmp/#{tmp_environment}'") master_opts = { 'main' => { 'environment' => tmp_environment, 'vendormoduledir' => vendor_modules_path } } with_puppet_running_on(master, master_opts) do agents.each do |agent| teardown do agent.rm_rf(vendor_modules_path) end # override vendormoduledir in case agent and server are on the same host agent_dir = get_test_file_path(agent, 'vendormodulepath') on(agent, puppet('agent', '-t', '--environment', tmp_environment, '--vendormoduledir', agent_dir), acceptable_exit_codes: [1]) do |result| assert_match(/Error: Failed to apply catalog: Resource type 'Mycustomtype' was not found/, result.stderr) end end end end end puppetlabs-puppet-789f600/acceptance/tests/agent/agent_parses_json_catalog.rb000066400000000000000000000015601470131746300275520ustar00rootroot00000000000000test_name "C99978: Agent parses a JSON catalog" tag 'risk:high', 'audit:high', # tests defined catalog format 'audit:integration', # There is no OS specific risk here. 'server', 'catalog:json' require 'puppet/acceptance/common_utils' require 'json' step "Agent parses a JSON catalog" do agents.each do |agent| # Path to a ruby binary ruby = Puppet::Acceptance::CommandUtils.ruby_command(agent) # Refresh the catalog on(agent, puppet("agent --test")) # The catalog file should be parseable JSON json_catalog = File.join(agent.puppet['client_datadir'], 'catalog', "#{agent.puppet['certname']}.json") on(agent, "cat #{json_catalog} | #{ruby} -rjson -e 'JSON.parse(STDIN.read)'") # Can the agent parse it as JSON? on(agent, puppet("catalog find --terminus json > /dev/null")) end end puppetlabs-puppet-789f600/acceptance/tests/agent/fallback_to_cached_catalog.rb000066400000000000000000000015401470131746300275740ustar00rootroot00000000000000test_name "fallback to the cached catalog" tag 'audit:high', 'audit:integration', # This test is not OS sensitive. 'audit:refactor' # A catalog fixture can be used for this test. Remove the usage of `with_puppet_running_on`. step "run agents once to cache the catalog" do with_puppet_running_on master, {} do on(agents, puppet("agent -t")) end end step "run agents again, verify they use cached catalog" do agents.each do |agent| # can't use --test, because that will set usecacheonfailure=false # We use a server that the agent can't possibly talk to in order # to guarantee that no communication can take place. on(agent, puppet("agent --onetime --no-daemonize --server puppet.example.com --verbose")) do |result| assert_match(/Using cached catalog/, result.stdout) unless agent['locale'] == 'ja' end end end puppetlabs-puppet-789f600/acceptance/tests/agent/last_run_summary_report.rb000066400000000000000000000113001470131746300273440ustar00rootroot00000000000000test_name "The 'last_run_summary.yaml' report has the right location and permissions" do tag 'audit:high' require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils agents.each do |agent| skip_test('This test does not work on Windows in japanese') if agent['platform'] =~ /windows/ && agent['locale'] == 'ja' custom_publicdir = agent.tmpdir('custom_public_dir') statedir = on(agent, puppet('config print statedir')).stdout.chomp fail_test("The 'statedir' config is not set!") if statedir.empty? publicdir = on(agent, puppet('config print publicdir')).stdout.chomp fail_test("The 'publicdir' config is not set!") if publicdir.empty? teardown do agent.rm_rf(custom_publicdir) agent.rm_rf("#{publicdir}/*") unless publicdir.empty? on(agent, puppet("config set publicdir #{publicdir}")) end step "Check if '#{publicdir}' was created during puppet installation" do on(agent, "ls #{publicdir}", :acceptable_exit_codes => [0]) end step "Check if '#{publicdir}' has '0755' permissions" do if agent['platform'] =~ /windows/ on(agent, "icacls #{publicdir}") do |result| # Linux 'Owner' permissions class equivalent assert_match(/BUILTIN\\Administrators:.*\(F\)/, result.stdout) # Known issue on Windows: 'C:\ProgramData\PuppetLabs\puppet' permissions are inherited # by its subfolders and it does not have any permissions for 'Everyone' (see 'PuppetAppDir' # in 'puppet-agent/resources/windows/wix/appdatafiles.wxs') # Below line should be added when solution is found: # assert_match(/Everyone:.*\(RX\)/, result.stdout) end else on(agent, "ls -al #{publicdir}") do |result| assert_match(/rwxr-xr-x.+\.$/, result.stdout) end end end step "Create the 'last_run_summary.yaml' report file by applying catalog" do on(agent, puppet('agent -t')) do |result| assert_match('Applied catalog', result.stdout) end end step "Check if the 'last_run_summary.yaml' report file created has '0640' permissions" do if agent['platform'] =~ /windows/ on(agent, "icacls #{File.join(publicdir, 'last_run_summary.yaml')}") do |result| # Linux 'Owner' premissions class equivalent assert_match('Administrator:(R,W', result.stdout) # Linux 'Group' permissions class equivalent assert_match('None:(R)', result.stdout) # Linux 'Public' permissions class equivalent assert_match('Everyone:(Rc,S,RA)', result.stdout) # According to icacls docs: # Rc = Read control # S = Synchronize # RA = Read attributes # More at https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/icacls end else on(agent, "ls -al #{publicdir}") do |result| assert_match(/rw-r-----.+last_run_summary\.yaml$/, result.stdout) end end end step "Check that '#{statedir}' exists and has no 'last_run_summary.yaml' file" do on(agent, "ls #{statedir}",:acceptable_exit_codes => [0]) do |result| refute_match(/last_run_summary.yaml/, result.stdout) end end step "Check that 'publicdir' can be reconfigured" do on(agent, puppet("config set publicdir #{custom_publicdir}")) on(agent, puppet('config print publicdir')) do |result| assert_match(custom_publicdir, result.stdout) end end step "Create a new 'last_run_summary.yaml' report file by applying catalog" do on(agent, puppet('agent -t')) do |result| assert_match('Applied catalog', result.stdout) end end step "Check if the 'last_run_summary.yaml' report file was created in the new location and still has '0640' permissions" do if agent['platform'] =~ /windows/ on(agent, "icacls #{File.join(custom_publicdir, 'last_run_summary.yaml')}") do |result| # Linux 'Owner' premissions class equivalent assert_match('Administrator:(R,W', result.stdout) # Linux 'Group' permissions class equivalent assert_match('None:(R)', result.stdout) # Linux 'Public' permissions class equivalent assert_match('Everyone:(Rc,S,RA)', result.stdout) # According to icacls docs: # Rc = Read control # S = Synchronize # RA = Read attributes # More at https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/icacls end else on(agent, "ls -al #{custom_publicdir}") do |result| assert_match(/rw-r-----.+last_run_summary\.yaml$/, result.stdout) end end end end end puppetlabs-puppet-789f600/acceptance/tests/aix/000077500000000000000000000000001470131746300215105ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/aix/aix_package_provider.rb000066400000000000000000000070051470131746300262050ustar00rootroot00000000000000test_name "aix package provider should work correctly" do tag 'audit:high', 'audit:acceptance' # OS specific by definition. confine :to, :platform => /aix/ dir = "/tmp/aix-packages-#{$$}" def assert_package_version(package, expected_version) # The output of lslpp is a colon-delimited list like: # sudo:sudo.rte:1.8.6.4: : :C: :Configurable super-user privileges runtime: : : : : : :0:0:/: # We want the version, so grab the third field on(hosts, "lslpp -qLc #{package} | cut -f3 -d:") do |result| actual_version = result.stdout.chomp assert_equal(expected_version, actual_version, "Installed package version #{actual_version} does not match expected version #{expected_version}") end end def get_package_manifest(package, version, sourcedir) <<-MANIFEST package { '#{package}': ensure => '#{version}', provider => aix, source => '#{sourcedir}', } MANIFEST end package = 'sudo.rte' version1 = '1.7.10.4' version2 = '1.8.6.4' teardown do on hosts, "rm -rf #{dir}" on hosts, "installp -u #{package}" end step "download packages to use for test" do on hosts, "mkdir -p #{dir}" on hosts, "curl https://artifactory.delivery.puppetlabs.net/artifactory/generic_enterprise__local/misc/sudo.#{version1}.aix51.lam.bff > #{dir}/sudo.#{version1}.aix51.lam.bff" on hosts, "curl https://artifactory.delivery.puppetlabs.net/artifactory/generic_enterprise__local/misc/sudo.#{version2}.aix51.lam.bff > #{dir}/sudo.#{version2}.aix51.lam.bff" end step "install the older version of package" do apply_manifest_on(hosts, get_package_manifest(package, version1, dir), :catch_failures => true) end step "verify package is installed and at the correct version" do assert_package_version package, version1 end step "install a newer version of the package" do apply_manifest_on(hosts, get_package_manifest(package, version2, dir), :catch_failures => true) end step "verify package is installed and at the newer version" do assert_package_version package, version2 end step "test that downgrading fails by trying to install an older version of the package" do apply_manifest_on(hosts, get_package_manifest(package, version1, dir), :acceptable_exit_codes => [4,6]) do |res| assert_match(/aix package provider is unable to downgrade packages/, res.stderr, "Didn't get an error about downgrading packages") end end step "uninstall the package" do apply_manifest_on(hosts, get_package_manifest(package, 'absent', dir), :catch_failures => true) end step "verify the package is gone" do on hosts, "lslpp -qLc #{package}", :acceptable_exit_codes => [1] end step "install the older version of package" do apply_manifest_on(hosts, get_package_manifest(package, version1, dir), :catch_failures => true) end step "verify package is installed and at the correct version" do assert_package_version package, version1 end step "install latest version of the package" do apply_manifest_on(hosts, get_package_manifest(package, 'latest', dir), :catch_failures => true) end step "verify package is installed and at the correct version" do assert_package_version package, version2 end step "PUP-7818 remove a package without defining the source metaparameter" do manifest = get_package_manifest(package, 'latest', dir) manifest = manifest + "package { 'nonexistant_example_package.rte': ensure => absent, }" apply_manifest_on(hosts, manifest, :catch_failures => true) end end puppetlabs-puppet-789f600/acceptance/tests/aix/nim_package_provider.rb000066400000000000000000000076151470131746300262160ustar00rootroot00000000000000test_name "NIM package provider should work correctly" tag 'audit:high', 'audit:acceptance' # OS specific by definition # nim test is slow, confine to only aix 7.2 and recent puppet versions confine :to, :platform => "aix" do |aix| version = on(aix, 'puppet --version').stdout version && Gem::Version.new(version) > Gem::Version.new('6.4.0') && on(aix, 'facter os.release.full').stdout == '7.2' end teardown do test_apply('cdrecord', 'absent', '') test_apply('puppet.test.rte', 'absent', '') end def assert_package_version(package, expected_version) # The output of lslpp is a colon-delimited list like: # sudo:sudo.rte:1.8.6.4: : :C: :Configurable super-user privileges runtime: : : : : : :0:0:/: # We want the version, so grab the third field on(hosts, "lslpp -qLc #{package} | cut -f3 -d:") do |result| actual_version = result.stdout.chomp assert_equal(expected_version, actual_version, "Installed package version #{actual_version} does not match expected version #{expected_version}") end end def get_manifest(package, ensure_value) < '#{ensure_value}', source => 'lpp_custom', provider => nim, } MANIFEST end def test_apply(package_name, ensure_value, expected_version) manifest = get_manifest(package_name, ensure_value) on hosts, puppet_apply(["--detailed-exitcodes", "--verbose"]), {:stdin => manifest, :acceptable_exit_codes => [2]} step "validate installed package version" do assert_package_version package_name, expected_version end step "run again to ensure idempotency" do on hosts, puppet_apply(["--detailed-exitcodes", "--verbose"]), {:stdin => manifest, :acceptable_exit_codes => [0]} end step "validate installed package version" do assert_package_version package_name, expected_version end end # These two packages live in an LPP source on the NIM master. Details # on our nim masters are available at # https://confluence.puppetlabs.com/display/OPS/IBM+Power+LPARs package_types = { "RPM" => { :package_name => "cdrecord", :old_version => '1.9-6', :new_version => '1.9-9' }, "BFF" => { :package_name => "puppet.test.rte", :old_version => '1.0.0.0', :new_version => '2.0.0.0' } } step "Setup: ensure test packages are not installed" do pkgs = ['cdrecord', 'puppet.test.rte'] pkgs.each do |pkg| on hosts, puppet_apply(["--detailed-exitcodes", "--verbose"]), {:stdin => get_manifest(pkg, 'absent'), :acceptable_exit_codes => [0,2]} end end package_types.each do |package_type, details| step "install a #{package_type} package via 'ensure=>present'" do package_name = details[:package_name] version = details[:new_version] test_apply(package_name, 'present', version) end step "uninstall a #{package_type} package via 'ensure=>absent'" do package_name = details[:package_name] version = '' test_apply(package_name, 'absent', version) end step "install a #{package_type} package via 'ensure=>'" do package_name = details[:package_name] version = details[:old_version] test_apply(package_name, version, version) end step "upgrade a #{package_type} package via 'ensure=>'" do package_name = details[:package_name] version = details[:new_version] test_apply(package_name, version, version) end step "attempt to downgrade a #{package_type} package via 'ensure=>'" do package_name = details[:package_name] version = details[:old_version] manifest = get_manifest(package_name, version) on(hosts, puppet_apply("--verbose", "--detailed-exitcodes"), { :stdin => manifest, :acceptable_exit_codes => [4,6] }) do |result| assert_match(/NIM package provider is unable to downgrade packages/, result.stderr, "Didn't get an error about downgrading packages") end end end puppetlabs-puppet-789f600/acceptance/tests/allow_arbitrary_node_name_fact_for_agent.rb000066400000000000000000000100501470131746300314730ustar00rootroot00000000000000test_name "node_name_fact should be used to determine the node name for puppet agent" tag 'audit:high', 'audit:integration', # Tests that the server properly overrides certname with node_name fact. # Testing of passenger master is no longer needed. 'server' success_message = "node_name_fact setting was correctly used to determine the node name" testdir = master.tmpdir("nodenamefact") node_names = [] on agents, facter('kernel') do |result| node_names << result.stdout.chomp end node_names.uniq! step "Prepare for custom tk-auth rules" do on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.conf /etc/puppetlabs/puppetserver/conf.d/auth.bak' modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => false}}) end teardown do modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => true}}) on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.bak /etc/puppetlabs/puppetserver/conf.d/auth.conf' on master, "service #{master['puppetservice']} reload" end step "Setup tk-auth rules" do tka_header = <<-HEADER authorization: { version: 1 rules: [ { match-request: { path: "/puppet/v3/file" type: path } allow: "*" sort-order: 500 name: "puppetlabs file" }, HEADER tka_node_rules = node_names.map do |node_name| <<-NODE_RULES { match-request: { path: "/puppet/v3/catalog/#{node_name}" type: path method: [get, post] } allow: "*" sort-order: 500 name: "puppetlabs catalog #{node_name}" }, { match-request: { path: "/puppet/v3/node/#{node_name}" type: path method: get } allow: "*" sort-order: 500 name: "puppetlabs node #{node_name}" }, { match-request: { path: "/puppet/v3/report/#{node_name}" type: path method: put } allow: "*" sort-order: 500 name: "puppetlabs report #{node_name}" }, NODE_RULES end tka_footer = <<-FOOTER { match-request: { path: "/" type: path } deny: "*" sort-order: 999 name: "puppetlabs deny all" } ] } FOOTER tk_auth = [tka_header, tka_node_rules, tka_footer].flatten.join("\n") apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) file { '/etc/puppetlabs/puppetserver/conf.d/auth.conf': ensure => file, mode => '0644', content => '#{tk_auth}', } MANIFEST end step "Setup site.pp for node name based classification" do site_manifest = <<-SITE_MANIFEST node default { notify { "false": } } node #{node_names.map { |name| %Q["#{name}"] }.join(", ")} { notify { "#{success_message}": } } SITE_MANIFEST apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) $directories = [ '#{testdir}', '#{testdir}/environments', '#{testdir}/environments/production', '#{testdir}/environments/production/manifests', ] file { $directories: ensure => directory, mode => '0755', } file { '#{testdir}/environments/production/manifests/manifest.pp': ensure => file, mode => '0644', content => '#{site_manifest}', } MANIFEST end step "Ensure nodes are classified based on the node name fact" do master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", }, 'master' => { 'node_terminus' => 'plain', }, } with_puppet_running_on(master, master_opts, testdir) do on(agents, puppet('agent', "--no-daemonize --verbose --onetime --node_name_fact kernel")) do |result| assert_match(/defined 'message'.*#{success_message}/, result.stdout) end end end puppetlabs-puppet-789f600/acceptance/tests/allow_arbitrary_node_name_for_agent.rb000066400000000000000000000072641470131746300305130ustar00rootroot00000000000000test_name "node_name_value should be used as the node name for puppet agent" tag 'audit:high', 'audit:integration', # Tests that the server properly overrides certname with node_name fact. # Testing of passenger master is no longer needed. 'server' success_message = "node_name_value setting was correctly used as the node name" testdir = master.tmpdir('nodenamevalue') step "Prepare for custom tk-auth rules" do on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.conf /etc/puppetlabs/puppetserver/conf.d/auth.bak' modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => false}}) end teardown do on master, 'cp /etc/puppetlabs/puppetserver/conf.d/auth.bak /etc/puppetlabs/puppetserver/conf.d/auth.conf' modify_tk_config(master, options['puppetserver-config'], {'jruby-puppet' => {'use-legacy-auth-conf' => true}}) on master, "service #{master['puppetservice']} reload" end step "Setup tk-auth rules" do tk_auth = <<-TK_AUTH authorization: { version: 1 rules: [ { match-request: { path: "/puppet/v3/file" type: path } allow: "*" sort-order: 500 name: "puppetlabs file" }, { match-request: { path: "/puppet/v3/catalog/specified_node_name" type: path method: [get, post] } allow: "*" sort-order: 500 name: "puppetlabs catalog" }, { match-request: { path: "/puppet/v3/node/specified_node_name" type: path method: get } allow: "*" sort-order: 500 name: "puppetlabs node" }, { match-request: { path: "/puppet/v3/report/specified_node_name" type: path method: put } allow: "*" sort-order: 500 name: "puppetlabs report" }, { match-request: { path: "/" type: path } deny: "*" sort-order: 999 name: "puppetlabs deny all" } ] } TK_AUTH apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) file { '/etc/puppetlabs/puppetserver/conf.d/auth.conf': ensure => file, mode => '0644', content => '#{tk_auth}', } MANIFEST end step "Setup site.pp for node name based classification" do site_manifest = <<-SITE_MANIFEST node default { notify { "false": } } node specified_node_name { notify { "#{success_message}": } } SITE_MANIFEST apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) $directories = [ '#{testdir}', '#{testdir}/environments', '#{testdir}/environments/production', '#{testdir}/environments/production/manifests', ] file { $directories: ensure => directory, mode => '0755', } file { '#{testdir}/environments/production/manifests/manifest.pp': ensure => file, mode => '0644', content => '#{site_manifest}', } MANIFEST end step "Ensure nodes are classified based on the node name fact" do master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", }, 'master' => { 'node_terminus' => 'plain', }, } with_puppet_running_on(master, master_opts, testdir) do on(agents, puppet('agent', "-t --node_name_value specified_node_name"), :acceptable_exit_codes => [0,2]) do |result| assert_match(/defined 'message'.*#{success_message}/, result.stdout) end end end puppetlabs-puppet-789f600/acceptance/tests/apply/000077500000000000000000000000001470131746300220545ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/apply/classes/000077500000000000000000000000001470131746300235115ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/apply/classes/parameterized_classes.rb000077500000000000000000000032121470131746300304100ustar00rootroot00000000000000test_name "parametrized classes" tag 'audit:high', 'audit:unit' # This should be covered at the unit layer. ######################################################################## step "should allow param classes" manifest = %q{ class x($y, $z) { notice("${y}-${z}") } class {x: y => '1', z => '2'} } apply_manifest_on(agents, manifest) do |result| fail_test "inclusion after parameterization failed" unless result.stdout.include? "1-2" end ######################################################################## # REVISIT: This was ported from the old set of tests, but I think that # the desired behaviour has recently changed. --daniel 2010-12-23 step "should allow param class post inclusion" manifest = %q{ class x($y, $z) { notice("${y}-${z}") } class {x: y => '1', z => '2'} include x } apply_manifest_on(agents, manifest) do |result| fail_test "inclusion after parameterization failed" unless result.stdout.include? "1-2" end ######################################################################## step "should allow param classes defaults" manifest = %q{ class x($y, $z='2') { notice("${y}-${z}") } class {x: y => '1'} } apply_manifest_on(agents, manifest) do |result| fail_test "the default didn't apply as expected" unless result.stdout.include? "1-2" end ######################################################################## step "should allow param class defaults to be overridden" manifest = %q{ class x($y, $z='2') { notice("${y}-${z}") } class {x: y => '1', z => '3'} } apply_manifest_on(agents, manifest) do |result| fail_test "the override didn't happen as we expected" unless result.stdout.include? "1-3" end puppetlabs-puppet-789f600/acceptance/tests/apply/classes/should_allow_param_override.rb000077500000000000000000000007261470131746300316210ustar00rootroot00000000000000test_name "should allow param override" tag 'audit:high', 'audit:unit' # This should be covered at the unit layer. manifest = %q{ class parent { notify { 'msg': message => parent, } } class child inherits parent { Notify['msg'] {message => 'child'} } include parent include child } apply_manifest_on(agents, manifest) do |result| fail_test "parameter override didn't work" unless result.stdout.include? "defined 'message' as 'child'" end puppetlabs-puppet-789f600/acceptance/tests/apply/classes/should_allow_param_undef_override.rb000077500000000000000000000020231470131746300327720ustar00rootroot00000000000000test_name "should allow overriding a parameter to undef in inheritence" tag 'audit:high', 'audit:unit' # This should be covered at the unit layer. agents.each do |agent| dir = agent.tmpdir('class_undef_override') out = File.join(dir, 'class_undef_override_out') source = File.join(dir, 'class_undef_override_test') manifest = %Q{ class parent { file { 'test': path => '#{out}', source => '#{source}', } } class child inherits parent { File['test'] { source => undef, content => 'hello new world!', } } include parent include child } step "prepare the target file on all systems" on(agent, "echo 'hello world!' > #{out}") step "apply the manifest" apply_manifest_on(agent, manifest) step "verify the file content" on(agent, "cat #{out}") do |result| fail_test "the file was not touched" if result.stdout.include? "hello world!" fail_test "the file was not updated" unless result.stdout.include? "hello new world" end on(agent, "rm -rf #{dir}") end puppetlabs-puppet-789f600/acceptance/tests/apply/classes/should_include_resources_from_class.rb000077500000000000000000000005501470131746300333440ustar00rootroot00000000000000test_name "resources declared in a class can be applied with include" tag 'audit:high', 'audit:unit' # This should be covered at the unit layer. manifest = %q{ class x { notify{'a':} } include x } apply_manifest_on(agents, manifest) do |result| fail_test "the resource did not apply" unless result.stdout.include?("defined 'message' as 'a'") end should_not_auto_include_resources_from_class.rb000077500000000000000000000006101470131746300351720ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/apply/classestest_name "resources declared in classes are not applied without include" tag 'audit:high', 'audit:unit' # This should be covered at the unit layer. manifest = %q{ class x { notify { 'test': message => 'never invoked' } } } apply_manifest_on(agents, manifest) do |result| fail_test "found the notify despite not including it" if result.stdout.include? "never invoked" end puppetlabs-puppet-789f600/acceptance/tests/catalog_with_binary_data.rb000066400000000000000000000057541470131746300262710ustar00rootroot00000000000000test_name "C100300: Catalog containing binary data is applied correctly" do require 'puppet/acceptance/common_utils' require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/agent_fqdn_utils' extend Puppet::Acceptance::AgentFqdnUtils tag 'risk:high', 'server' test_num = 'c100300' tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*')) agent_tmp_dirs = {} agents.each do |agent| agent_tmp_dirs[agent_to_fqdn(agent)] = agent.tmpdir(tmp_environment) end teardown do step 'remove all test files on agents' do agents.each {|agent| on(agent, "rm -r '#{agent_tmp_dirs[agent_to_fqdn(agent)]}'", :accept_all_exit_codes => true)} end # Remove all traces of the last used environment agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end # note - master teardown is registered by #mk_tmp_environment_with_teardown end step "Create module with binary data file on master" do on(master, "mkdir -p '#{environmentpath}/#{tmp_environment}/modules/#{test_num}'/{manifests,files}") master_module_manifest = "#{environmentpath}/#{tmp_environment}/modules/#{test_num}/manifests/init.pp" master_module_binary_file = "#{environmentpath}/#{tmp_environment}/modules/#{test_num}/files/binary_data" create_remote_file(master, master_module_binary_file, "\xC0\xFF") on(master, "chmod 644 '#{master_module_binary_file}'") manifest = <<-MANIFEST class #{test_num}( ) { \$test_path = \$facts['networking']['fqdn'] ? #{agent_tmp_dirs} file { '#{test_num}': path => "\$test_path/#{test_num}", content => binary_file('#{test_num}/binary_data'), ensure => present, } } MANIFEST create_remote_file(master, master_module_manifest, manifest) on(master, "chmod 644 '#{master_module_manifest}'") end step "Create site.pp to classify nodes to include module" do site_pp_file = "#{environmentpath}/#{tmp_environment}/manifests/site.pp" site_pp = <<-SITE_PP node default { include #{test_num} } SITE_PP create_remote_file(master, site_pp_file, site_pp) on(master, "chmod 644 '#{site_pp_file}'") end step "start the master" do with_puppet_running_on(master, {}) do step "run puppet and ensure that binary data was correctly applied" do agents.each do |agent| on(agent, puppet('agent', '--test', "--environment '#{tmp_environment}'"), :acceptable_exit_codes => 2) on(agent, "#{Puppet::Acceptance::CommandUtils::ruby_command(agent)} -e 'puts File.binread(\"#{agent_tmp_dirs[agent_to_fqdn(agent)]}/#{test_num}\").bytes.map {|b| b.to_s(16)}'") do |res| assert_match(/c0\nff/, res.stdout, 'Binary file did not contain originally specified data') end end end end end end puppetlabs-puppet-789f600/acceptance/tests/direct_puppet/000077500000000000000000000000001470131746300235765ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/direct_puppet/cached_catalog_remediate_local_drift.rb000066400000000000000000000106321470131746300334070ustar00rootroot00000000000000require 'puppet/acceptance/static_catalog_utils' extend Puppet::Acceptance::StaticCatalogUtils test_name "PUP-5122: Puppet remediates local drift using code_id and content_uri" do tag 'audit:high', 'audit:acceptance', 'audit:refactor', # use mk_tmp_environment_with_teardown helper for environment construction 'server' skip_test 'requires puppetserver installation' if @options[:type] != 'aio' basedir = master.tmpdir(File.basename(__FILE__, '.*')) module_dir = "#{basedir}/environments/production/modules" master_opts = { 'main' => { 'environmentpath' => "#{basedir}/environments" } } step "Add versioned-code parameters to puppetserver.conf and ensure the server is running" do setup_puppetserver_code_id_scripts(master, basedir) end teardown do cleanup_puppetserver_code_id_scripts(master, basedir) on master, "rm -rf #{basedir}" agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "Create a module and a file with content representing the first code_id version" do apply_manifest_on(master, < true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{basedir}':; '#{basedir}/environments':; '#{basedir}/environments/production':; '#{basedir}/environments/production/manifests':; '#{module_dir}':; '#{module_dir}/foo':; '#{module_dir}/foo/files':; } MANIFEST end with_puppet_running_on master, master_opts, basedir do agents.each do |agent| agent_test_file_path = agent.tmpfile('foo_file') step "Add test file resource to site.pp on master with agent-specific file path" do apply_manifest_on(master, < true) File { owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { "#{basedir}/environments/production/manifests/site.pp" : ensure => file, mode => "0640", content => "node default { file { '#{agent_test_file_path}' : ensure => file, source => 'puppet:///modules/foo/foo.txt' } }", } file { "#{module_dir}/foo/files/foo.txt" : ensure => file, content => "code_version_1", mode => "0640", } MANIFEST end step "agent: #{agent}: Initial run: create the file with code version 1 and cache the catalog" on(agent, puppet("agent", "-t"), :acceptable_exit_codes => [0,2]) # When there is no drift, there should be no request made to the server # for file metadata or file content. A puppet run depending on # a non-server will fail if such a request is made. Verify the agent # sends a report. step "Remove existing reports from server reports directory" on(master, "rm -rf /opt/puppetlabs/server/data/puppetserver/reports/#{agent.node_name}/*") r = on(master, "ls /opt/puppetlabs/server/data/puppetserver/reports/#{agent.node_name} | wc -l").stdout.chomp assert_equal(r, '0', "reports directory should be empty!") step "Verify puppet run without drift does not make file request from server" r = on(agent, puppet("agent", "--use_cached_catalog", "--server", "no_such_host", "--report_server", master.hostname, "--onetime", "--no-daemonize", "--detailed-exitcodes", "--verbose" )).stderr assert_equal(r, "", "Fail: Did agent try to contact server?") step "Verify report was delivered to server" r = on(master, "ls /opt/puppetlabs/server/data/puppetserver/reports/#{agent.node_name} | wc -l").stdout.chomp assert_equal(r, '1', "Reports directory should have one file") step "agent: #{agent}: Remove the test file to simulate drift" on(agent, "rm -rf #{agent_test_file_path}") step "Alter the source file on the master to simulate a code update" apply_manifest_on(master, < true) file { "#{module_dir}/foo/files/foo.txt" : ensure => file, mode => "0640", content => "code_version_2", } MANIFEST step "Run agent again using --use_cached_catalog and ensure content from the first code_id is used" on(agent, puppet("agent", "-t", "--use_cached_catalog"), :acceptable_exit_codes => [0,2]) on(agent, "cat #{agent_test_file_path}") do |result| assert_equal('code_version_1', result.stdout) end end end end catalog_uuid_correlates_catalogs_with_reports.rb000066400000000000000000000051251470131746300354200ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/direct_puppettest_name "PUP-5872: catalog_uuid correlates catalogs with reports" do tag 'audit:high', 'audit:acceptance', 'audit:refactor' # remove dependence on server by adding a # catalog and report fixture to validate against. master_reportdir = create_tmpdir_for_user(master, 'reportdir') def remove_reports_on_master(master_reportdir, agent_node_name) on(master, "rm -rf #{master_reportdir}/#{agent_node_name}/*") end def get_catalog_uuid_from_cached_catalog(host, agent_vardir, agent_node_name) cache_catalog_uuid = nil on(host, "cat #{agent_vardir}/client_data/catalog/#{agent_node_name}.json") do |result| cache_catalog_uuid = result.stdout.match(/"catalog_uuid":"([a-z0-9\-]*)",/)[1] end cache_catalog_uuid end def get_catalog_uuid_from_report(master_reportdir, agent_node_name) report_catalog_uuid = nil on(master, "cat #{master_reportdir}/#{agent_node_name}/*") do |result| report_catalog_uuid = result.stdout.match(/catalog_uuid: '?([a-z0-9\-]*)'?/)[1] end report_catalog_uuid end with_puppet_running_on(master, :master => { :reportdir => master_reportdir, :reports => 'store' }) do agents.each do |agent| agent_vardir = agent.tmpdir(File.basename(__FILE__, '.*')) step "agent: #{agent}: Initial run to retrieve a catalog and generate the first report" do on(agent, puppet("agent", "-t", "--vardir #{agent_vardir}"), :acceptable_exit_codes => [0,2]) end cache_catalog_uuid = get_catalog_uuid_from_cached_catalog(agent, agent_vardir, agent.node_name) step "agent: #{agent}: Ensure the catalog and report share the same catalog_uuid" do report_catalog_uuid = get_catalog_uuid_from_report(master_reportdir, agent.node_name) assert_equal(cache_catalog_uuid, report_catalog_uuid, "catalog_uuid found in cached catalog, #{cache_catalog_uuid} did not match report #{report_catalog_uuid}") end step "cleanup reports on master" do remove_reports_on_master(master_reportdir, agent.node_name) end step "Run with --use_cached_catalog and ensure catalog_uuid in the new report matches the cached catalog" do on(agent, puppet("agent", "--onetime", "--no-daemonize", "--use_cached_catalog", "--vardir #{agent_vardir}"), :acceptance_exit_codes => [0,2]) report_catalog_uuid = get_catalog_uuid_from_report(master_reportdir, agent.node_name) assert_equal(cache_catalog_uuid, report_catalog_uuid, "catalog_uuid found in cached catalog, #{cache_catalog_uuid} did not match report #{report_catalog_uuid}") end end end end puppetlabs-puppet-789f600/acceptance/tests/direct_puppet/static_catalog_env_control.rb000066400000000000000000000220111470131746300315100ustar00rootroot00000000000000test_name "Environment control of static catalogs" tag 'audit:high', 'audit:acceptance', 'audit:refactor', # use mk_tmp_environment_with_teardown helper for environment construction 'server' skip_test 'requires puppetserver to test static catalogs' if @options[:type] != 'aio' require 'json' @testroot = master.tmpdir(File.basename(__FILE__, '/*')) @coderoot = "#{@testroot}/code" @confdir = master['puppetserver-confdir'] @master_opts = { 'main' => { 'environmentpath' => "#{@coderoot}/environments", }, } @production_files = {} @canary_files = {} @agent_manifests = {} @catalog_files = {} agents.each do |agent| hn = agent.node_name resdir = agent.tmpdir('results') @production_files[hn] = "#{resdir}/prod_hello_from_puppet_uri" @canary_files[hn] = "#{resdir}/can_hello_from_puppet_uri" @catalog_files[hn] = "#{on(agent, puppet('config', 'print', 'client_datadir')).stdout.chomp}/catalog/#{hn}.json" @agent_manifests[hn] = < file, mode => "0644", content => "class hello { notice('hello from production-hello') file { '#{resdir}' : ensure => directory, mode => '0755', } file { '#{resdir}/prod_hello_from_puppet_uri' : ensure => file, mode => '0644', source => 'puppet:///modules/hello/hello_msg', } }", } file { '#{@coderoot}/environments/canary/modules/can_hello/manifests/init.pp': ensure => file, mode => "0644", content => 'class can_hello { notice("hello from production-hello") file { "#{resdir}": ensure => directory, mode => "0755", } file { "#{resdir}/can_hello_from_puppet_uri" : ensure => file, mode => "0644", source => "puppet:///modules/can_hello/hello_msg", } }', } MANIFESTAGENT end # The code_content script needs to return the correct content whose checksum # matches the metadata contained in the static catalog. PRODUCTION_CONTENT = "Hello message from production/hello module, content from source attribute.".freeze CANARY_CONTENT = "Hello message from canary/can_hello module, content from source attribute.".freeze @manifest = < directory, mode => "0755", } file { '#{@testroot}':; '#{@coderoot}':; '#{@coderoot}/environments':; '#{@coderoot}/environments/production':; '#{@coderoot}/environments/production/manifests':; '#{@coderoot}/environments/production/modules':; '#{@coderoot}/environments/production/modules/hello':; '#{@coderoot}/environments/production/modules/hello/manifests':; '#{@coderoot}/environments/production/modules/hello/files':; '#{@coderoot}/environments/canary':; '#{@coderoot}/environments/canary/manifests':; '#{@coderoot}/environments/canary/modules':; '#{@coderoot}/environments/canary/modules/can_hello':; '#{@coderoot}/environments/canary/modules/can_hello/manifests':; '#{@coderoot}/environments/canary/modules/can_hello/files':; } file { '#{@coderoot}/code_id.sh' : ensure => file, mode => "0755", content => '#! /bin/bash echo "code_version_1" ', } file { '#{@coderoot}/code_content.sh' : ensure => file, mode => "0755", content => '#! /bin/bash # script arguments: # $1 environment # $2 code_id # $3 path relative to mount # use echo -n to omit newline if [ $1 == "production" ] ; then echo -n "#{PRODUCTION_CONTENT}" else echo -n "#{CANARY_CONTENT}" fi ', } file { '#{@coderoot}/environments/production/environment.conf': ensure => file, mode => "0644", content => 'environment_timeout = 0 ', } file { '#{@coderoot}/environments/canary/environment.conf': ensure => file, mode => "0644", content => 'environment_timeout = 0 static_catalogs = false ', } file { '#{@coderoot}/environments/production/manifests/site.pp': ensure => file, mode => "0644", content => "node default { include hello } ", } file { '#{@coderoot}/environments/canary/manifests/site.pp': ensure => file, mode => "0644", content => "node default { include can_hello } ", } file { '#{@coderoot}/environments/production/modules/hello/files/hello_msg': ensure => file, mode => "0644", content => "#{PRODUCTION_CONTENT}", } file { '#{@coderoot}/environments/canary/modules/can_hello/files/hello_msg': ensure => file, mode => "0644", content => "#{CANARY_CONTENT}", } MANIFEST teardown do on(master, "mv #{@confdir}/puppetserver.conf.bak #{@confdir}/puppetserver.conf") on(master, "rm -rf #{@testroot}") agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step 'apply main manifest, static_catalogs unspecified in global scope, unspecified in production environment, disabled in canary environment' on( master, "cp #{@confdir}/puppetserver.conf #{@confdir}/puppetserver.conf.bak" ) apply_manifest_on(master, @manifest, :catch_failures => true) step "Add versioned-code parameters to puppetserver.conf and ensure the server is running" puppetserver_config = "#{master['puppetserver-confdir']}/puppetserver.conf" on master, "cp #{puppetserver_config} #{@coderoot}/puppetserver.conf.bak" versioned_code_settings = { "jruby-puppet" => { "master-code-dir" => @coderoot }, "versioned-code" => { "code-id-command" => "#{@coderoot}/code_id.sh", "code-content-command" => "#{@coderoot}/code_content.sh" } } modify_tk_config(master, puppetserver_config, versioned_code_settings) step 'start puppet server' with_puppet_running_on master, @master_opts, @coderoot do agents.each do |agent| hn = agent.node_name apply_manifest_on(master, @agent_manifests[hn], :catch_failures => true) step 'agent gets a production catalog, should be static catalog by default' on( agent, puppet( 'agent', '-t', '--environment', 'production' ), :acceptable_exit_codes => [0, 2] ) step 'verify production environment' r = on(agent, "cat #{@catalog_files[hn]}") catalog_content = JSON.parse(r.stdout) assert_equal( catalog_content['environment'], 'production', 'catalog for unexpectected environment' ) step 'verify static catalog by finding metadata section in catalog' assert( catalog_content['metadata'] && catalog_content['metadata'][@production_files[hn]], 'metadata section of catalog not found' ) step 'agent gets a canary catalog, static catalog should be disabled' on( agent, puppet( 'agent', '-t', '--environment', 'canary' ), :acceptable_exit_codes => [0, 2] ) step 'verify canary environment' r = on(agent, "cat #{@catalog_files[hn]}") catalog_content = JSON.parse(r.stdout) assert_equal( catalog_content['environment'], 'canary', 'catalog for unexpectected environment' ) step 'verify not static catalog by absence of metadata section in catalog' assert_nil( catalog_content['metadata'], 'unexpected metadata section found in catalog' ) end end step 'enable static catalog for canary environment' @static_canary_manifest = < file, mode => "0644", content => 'environment_timeout = 0 static_catalogs = true ', } MANIFEST2 apply_manifest_on(master, @static_canary_manifest, :catch_failures => true) step 'disable global static catalog setting' @master_opts = { 'master' => { 'static_catalogs' => false }, 'main' => { 'environmentpath' => "#{@coderoot}/environments", }, } step 'bounce server for static catalog disable setting to take effect.' with_puppet_running_on master, @master_opts, @coderoot do agents.each do |agent| hn = agent.node_name apply_manifest_on(master, @agent_manifests[hn], :catch_failures => true) step 'agent gets a production catalog, should not be a static catalog' on( agent, puppet( 'agent', '-t', '--environment', 'production' ), :acceptable_exit_codes => [0, 2] ) step 'verify production environment' r = on(agent, "cat #{@catalog_files[hn]}") catalog_content = JSON.parse(r.stdout) assert_equal( catalog_content['environment'], 'production', 'catalog for unexpectected environment' ) step 'verify production environment, not static catalog' assert_nil( catalog_content['metadata'], 'unexpected metadata section found in catalog' ) step 'agent gets a canary catalog, static catalog should be enabled' on( agent, puppet( 'agent', '-t', '--environment', 'canary' ), :acceptable_exit_codes => [0, 2] ) step 'verify canary catalog' r = on(agent, "cat #{@catalog_files[hn]}") catalog_content = JSON.parse(r.stdout) assert_equal( catalog_content['environment'], 'canary', 'catalog for unexpectected environment' ) step 'verify canary static catalog' assert( catalog_content['metadata'] && catalog_content['metadata'][@canary_files[hn]], 'metadata section of catalog not found' ) end end puppetlabs-puppet-789f600/acceptance/tests/direct_puppet/supports_utf8.rb000066400000000000000000000054371470131746300270010ustar00rootroot00000000000000test_name "C97172: static catalogs support utf8" do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/agent_fqdn_utils' extend Puppet::Acceptance::AgentFqdnUtils tag 'audit:high', 'audit:acceptance', 'audit:refactor' # Review for agent side UTF validation. app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) tmp_file = {} agents.each do |agent| tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment) end teardown do # Remove all traces of the last used environment agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end step 'clean out produced resources' do agents.each do |agent| if tmp_file.has_key?(agent_to_fqdn(agent)) && !tmp_file[agent_to_fqdn(agent)].empty? on(agent, "rm -f '#{tmp_file[agent_to_fqdn(agent)]}'") end end end end file_contents = 'Mønti Pythøn ik den Hølie Gräilen, yër? € ‰ ㄘ 万 竹 Ü Ö' step 'create site.pp with utf8 chars' do manifest = < file, content => ' \$test_path = \$facts["networking"]["fqdn"] ? #{tmp_file} file { \$test_path: content => @(UTF8) #{file_contents} | UTF8 } ', } MANIFEST apply_manifest_on(master, manifest, :catch_failures => true) end step 'run agent(s)' do with_puppet_running_on(master, {}) do agents.each do |agent| config_version = '' config_version_matcher = /configuration version '(\d+)'/ on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 2).stdout do |result| config_version = result.match(config_version_matcher)[1] end on(agent, "cat '#{tmp_file[agent_to_fqdn(agent)]}'").stdout do |result| assert_equal(file_contents, result, 'file contents did not match accepted') end on(agent, "rm -f '#{tmp_file[agent_to_fqdn(agent)]}'") on(agent, puppet("agent -t --environment '#{tmp_environment}' --use_cached_catalog"), :acceptable_exit_codes => 2).stdout do |result| assert_match(config_version_matcher, result, 'agent did not use cached catalog') second_config_version = result.match(config_version_matcher)[1] asset_equal(config_version, second_config_version, 'config version should have been the same') end on(agent, "cat '#{tmp_file[agent_to_fqdn(agent)]}'").stdout do |result| assert_equal(file_contents, result, 'file contents did not match accepted') end end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/000077500000000000000000000000001470131746300232735ustar00rootroot00000000000000broken_unassigned_environment_handled_gracefully.rb000066400000000000000000000035111470131746300355610ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/environmenttest_name 'PUP-3755 Test an un-assigned broken environment' tag 'audit:high', 'audit:integration', 'audit:refactor', # Use mk_tmp_environment_with_teardown helper 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step 'setup environments' testdir = create_tmpdir_for_user(master, 'confdir') environment = 'debug' manifest = <<-MANIFEST File { ensure => directory, owner => #{master.puppet['user']}, group => #{master.puppet['group']}, mode => "0750", } file { "#{testdir}":; "#{testdir}/environments":; "#{testdir}/environments/production":; "#{testdir}/environments/production/manifests":; "#{testdir}/environments/production/modules":; "#{testdir}/environments/#{environment}":; "#{testdir}/environments/#{environment}/manifests":; "#{testdir}/environments/#{environment}/modules":; } # broken envioronment file { "#{testdir}/environments/production/manifests/site.pp": ensure => file, content => 'import "/tmp/bogus/*.pp"' } file { "#{testdir}/environments/#{environment}/manifests/site.pp": ensure => file, content => 'node default{\nnotify{"you win":}\n}' } MANIFEST apply_manifest_on(master, manifest, :catch_failures => true) step 'run agents, ensure no one complains about the other environment' master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments" } } with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| on(agent, puppet('agent', "--test --environment #{environment}"), :acceptable_exit_codes => (0..255)) do |result| assert_match(/you win/, result.stdout, 'agent did not pickup newly classified environment.') end end end puppetlabs-puppet-789f600/acceptance/tests/environment/can_enumerate_environments.rb000066400000000000000000000040601470131746300312350ustar00rootroot00000000000000test_name "Can enumerate environments via an HTTP endpoint" tag 'audit:high', 'audit:integration', 'server' confine :except, :platform => /osx/ # see PUP-4820 def server_port(agent) setting_on(agent, "agent", "serverport") end def setting_on(host, section, name) on(host, puppet("config", "print", name, "--section", section)).stdout.chomp end def full_path(host, path) if host['platform'] =~ /win/ on(host, "cygpath '#{path}'").stdout.chomp else path end end def curl_master_from(agent, path, headers = '', &block) url = "https://#{master}:#{server_port(agent)}#{path}" cert_path = full_path(agent, setting_on(agent, "agent", "hostcert")) key_path = full_path(agent, setting_on(agent, "agent", "hostprivkey")) curl_base = "curl --tlsv1 -sg --cert \"#{cert_path}\" --key \"#{key_path}\" -k -H '#{headers}'" on agent, "#{curl_base} '#{url}'", &block end master_user = puppet_config(master, 'user', section: 'master') environments_dir = create_tmpdir_for_user master, "environments" apply_manifest_on(master, <<-MANIFEST) File { ensure => directory, owner => #{master_user}, group => #{master.puppet['group']}, mode => "0770", } file { "#{environments_dir}":; "#{environments_dir}/env1":; "#{environments_dir}/env2":; } MANIFEST master_opts = { :master => { :environmentpath => environments_dir } } if master.is_pe? master_opts[:master][:basemodulepath] = master['sitemoduledir'] end with_puppet_running_on(master, master_opts) do step "Ensure that an unauthenticated client cannot access the environments list" do on(master, "curl --tlsv1 -ksv https://#{master}:#{server_port(master)}/puppet/v3/environments", :acceptable_exit_codes => [0,7]) do |result| assert_match(/< HTTP\/1\.\d 403/, result.stderr) end end step "Ensure that an authenticated client can retrieve the list of environments" do curl_master_from(master, '/puppet/v3/environments') do |result| data = JSON.parse(result.stdout) assert_equal(["env1", "env2", "production"], data["environments"].keys.sort) end end end puppetlabs-puppet-789f600/acceptance/tests/environment/custom_type_provider_from_same_environment.rb000066400000000000000000000113731470131746300345660ustar00rootroot00000000000000test_name 'C59122: ensure provider from same env as custom type' do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:integration', # This behavior is specific to the master to 'do the right thing' 'server' app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) file_correct = "#{tmp_environment}-correct.txt" file_wrong = "#{tmp_environment}-wrong.txt" fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" fq_prod_environmentpath = "#{environmentpath}/production" teardown do step 'clean out production env' do on(master, "rm -rf #{fq_prod_environmentpath}/modules/*", :accept_all_exit_codes => true) on(master, "rm #{fq_prod_environmentpath}/manifests/site.pp", :accept_all_exit_codes => true) end step 'clean out file resources' do on(hosts, "rm #{file_correct} #{file_wrong}", :accept_all_exit_codes => true) end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "create a custom type and provider in each of production and #{tmp_environment}" do type_name = 'test_custom_type' provider_name = 'universal' type_content = < directory } file { '#{fq_tmp_environmentpath}/modules/simple_type':; '#{fq_tmp_environmentpath}/modules/simple_type/lib':; '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet':; '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/type/':; '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/provider/':; '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}':; '#{fq_prod_environmentpath}/modules':; '#{fq_prod_environmentpath}/modules/simple_type':; '#{fq_prod_environmentpath}/modules/simple_type/lib':; '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet':; '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/type/':; '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/provider/':; '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}':; } file { '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/type/#{type_name}.rb': ensure => file, content => '#{type_content}', } file { '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/type/#{type_name}.rb': ensure => file, content => '#{type_content}', } file { '#{fq_tmp_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}/#{provider_name}.rb': ensure => file, content => '#{provider_content('correct', type_name, provider_name)}', } file { '#{fq_prod_environmentpath}/modules/simple_type/lib/puppet/provider/#{type_name}/#{provider_name}.rb': ensure => file, content => '#{provider_content('wrong', type_name, provider_name)}', } file { '#{fq_tmp_environmentpath}/manifests/site.pp': ensure => file, content => 'node default { #{type_name}{"#{file_correct}": ensure=>present} }', } file { '#{fq_prod_environmentpath}/manifests': } file { '#{fq_prod_environmentpath}/manifests/site.pp': ensure => file, content => 'node default { #{type_name}{"#{file_wrong}": ensure=>present} }', } MANIFEST apply_manifest_on(master, manifest, :catch_failures => true) end step "run agent in #{tmp_environment}, ensure it finds the correct provider" do with_puppet_running_on(master,{}) do agents.each do |agent| on(agent, puppet("agent -t --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert_equal(2, result.exit_code, 'agent did not exit with the correct code of 2') assert_match(/#{file_correct}/, result.stdout, 'agent did not ensure the correct file') assert(agent.file_exist?(file_correct), 'puppet did not create the file') end end end end end directory_environment_production_created_master.rb000066400000000000000000000021151470131746300355000ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/environmenttest_name 'ensure production environment created by master if missing' tag 'audit:high', 'audit:integration', 'server' testdir = create_tmpdir_for_user master, 'prod-env-created' step 'make environmentpath' master_user = puppet_config(master, 'user', section: 'master') apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, owner => #{master_user}, group => #{master.puppet['group']}, mode => '0640', } file { "#{testdir}":; "#{testdir}/environments":; } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", } } step 'run master; ensure production environment created' with_puppet_running_on(master, master_opts, testdir) do on(master, "test -d '#{testdir}/environments/production'") step 'ensure catalog returned from production env with no changes' agents.each do |agent| on(agent, puppet("agent -t --environment production --detailed-exitcodes")) do |result| # detailed-exitcodes produces a 0 when no changes are made. assert_equal(0, result.exit_code) end end end puppetlabs-puppet-789f600/acceptance/tests/environment/enc_nonexistent_directory_environment.rb000066400000000000000000000045231470131746300335370ustar00rootroot00000000000000test_name "Master should produce error if enc specifies a nonexistent environment" do require 'puppet/acceptance/classifier_utils.rb' extend Puppet::Acceptance::ClassifierUtils tag 'audit:high', 'audit:unit', 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end testdir = create_tmpdir_for_user(master, 'nonexistent_env') apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, owner => #{master.puppet['user']}, group => #{master.puppet['group']}, mode => '0755', } file { "#{testdir}":; "#{testdir}/environments":; "#{testdir}/environments/production":; "#{testdir}/environments/production/manifests":; "#{testdir}/environments/production/manifests/site.pp": ensure => file, mode => '0644', content => 'notify { "In the production environment": }'; } MANIFEST if master.is_pe? group = { 'name' => 'Environment Does Not Exist', 'description' => 'Classify our test agent nodes in an environment that does not exist.', 'environment' => 'doesnotexist', 'environment_trumps' => true, } create_group_for_nodes(agents, group) else apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) file { "#{testdir}/enc.rb": ensure => file, mode => '0775', content => '#!#{master['privatebindir']}/ruby puts "environment: doesnotexist" '; } MANIFEST end master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", } } master_opts['master'] = { 'node_terminus' => 'exec', 'external_nodes' => "#{testdir}/enc.rb", } if !master.is_pe? with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| on(agent, puppet("agent -t --verbose"), :acceptable_exit_codes => [1]) do |result| unless agent['locale'] == 'ja' assert_match(/Could not find a directory environment named 'doesnotexist'/, result.stderr, "Errors when nonexistent environment is specified") end refute_match(/In the production environment/, result.stdout, "Executed manifest from production environment") end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/environment_scenario-bad.rb000066400000000000000000000046041470131746300305770ustar00rootroot00000000000000test_name 'Test behavior of directory environments when environmentpath is set to a non-existent directory' do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/classifier_utils' extend Puppet::Acceptance::ClassifierUtils tag 'audit:high', 'audit:unit', # The error responses for the agent should be covered by Ruby unit tests. # The server 404/400 response should be covered by server integration tests. 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end classify_nodes_as_agent_specified_if_classifer_present step 'setup environments' testdir = create_tmpdir_for_user(master, 'confdir') puppet_conf_backup_dir = create_tmpdir_for_user(master, "puppet-conf-backup-dir") apply_manifest_on(master, environment_manifest(testdir), :catch_failures => true) step 'Test' do env_path = '/doesnotexist' master_opts = { 'main' => { 'environmentpath' => "#{env_path}", } } env = 'testing' results = use_an_environment(env, 'bad environmentpath', master_opts, testdir, puppet_conf_backup_dir, :directory_environments => true) expectations = { :puppet_config => { :exit_code => 0, :matches => [%r{basemodulepath = /etc/puppetlabs/code/modules:/opt/puppetlabs/puppet/modules}, %r{modulepath =}, %r{manifest =}, %r{config_version =}], }, :puppet_apply => { :exit_code => 1, :matches => [%r{Could not find a directory environment named '#{env}' anywhere in the path.*#{env_path}}], }, :puppet_agent => { :exit_code => 0, }, } agents.each do |host| unless host['locale'] == 'ja' expectations[:puppet_agent][:matches] = [%r{Environment '#{env}' not found on server, skipping initial pluginsync.}, %r{Local environment: '#{env}' doesn't match server specified environment 'production', restarting agent run with environment 'production'}] end end assert_review(review_results(results, expectations)) end end puppetlabs-puppet-789f600/acceptance/tests/environment/feature_branch_configured_environment.rb000066400000000000000000000015221470131746300334210ustar00rootroot00000000000000test_name "Agent should use set environment after running with specified environment" do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'server' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end tmp_environment = mk_tmp_environment_with_teardown(master, 'special') agents.each do |agent| on(agent, puppet("agent -t --environment #{tmp_environment}")) do |result| assert_match(/Info: Using environment 'special_\w+'/, result.stdout) end on(agent, puppet('agent -t')) do |result| assert_match(/Info: Using environment 'production'/, result.stdout) end end end should_find_existing_production_environment.rb000066400000000000000000000125561470131746300346540ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/environmenttest_name "should find existing production environment" tag 'audit:medium' require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils agents.each do |agent| path_separator = agent.platform_defaults[:pathseparator] initial_environment = on(agent, puppet("config print environment")).stdout.chomp initial_environment_paths = on(agent, puppet("config print environmentpath")).stdout.chomp.split(path_separator) default_environment_path = '' custom_environment_path = agent.tmpdir('custom_environment') teardown do step 'uninstall the module' do uninstall_i18n_demo_module(master) uninstall_i18n_demo_module(agent) end step 'Remove custom environment paths' do environment_paths = on(agent, puppet("config print environmentpath")).stdout.chomp environment_paths.split(path_separator).each do |path| agent.rm_rf(path) unless initial_environment_paths.include?(path) end agent.rm_rf(custom_environment_path) end step 'Reset environment settings' do on(agent, puppet("config set environmentpath #{initial_environment_paths.join(path_separator)}")) on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end if initial_environment == 'production' on(agent, puppet("config delete environment")) else on(agent, puppet("config set environment #{initial_environment}")) end on(agent, puppet("agent -t")) end end step 'Ensure a clean environment with default settings' do step 'Remove the lastrunfile which contains the last used agent environment' do on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end step 'Change to the default environment setting' do on(agent, puppet("config delete environment")) on(agent, puppet("config print environment")) do |result| assert_match('production', result.stdout, "Default environment is not 'production' as expected") end end step 'Change to the default environmentpath setting and remove production folder' do on(agent, puppet("config delete environmentpath")) default_environment_path = on(agent, puppet("config print environmentpath")).stdout.chomp agent.rm_rf("#{default_environment_path}/production") end step 'Apply changes and expect puppet to create the production folder back' do on(agent, puppet("agent -t")) on(agent, "ls #{default_environment_path}") do |result| assert_match('production', result.stdout, "Default environment folder was not generated in last puppet run") end end end step 'Install a module' do install_i18n_demo_module(master) end step 'Expect output from the custom fact of the module' do on(agent, puppet("agent -t"), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/Error:.*i18ndemo/, result.stderr) end end step 'Add a custom environment path before the current one' do current_environment_path = on(agent, puppet("config print environmentpath")).stdout.chomp on(agent, puppet("config set environmentpath '#{custom_environment_path}#{path_separator}#{current_environment_path}'")) end step 'Expect the module to still be found' do on(agent, puppet("agent -t"), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/Error:.*i18ndemo/, result.stderr) end end step 'Expect no production environment folder changes' do on(agent, "ls #{custom_environment_path}") do |result| refute_match(/production/, result.stdout) end on(agent, "ls #{default_environment_path}") do |result| assert_match('production', result.stdout) end end step 'Remove production folder' do agent.rm_rf("#{default_environment_path}/production") end step 'Expect production environment folder to be recreated in the custom path' do on(agent, puppet("agent -t"), :acceptable_exit_codes => [0, 2]) do |result| step 'Expect the module to be gone on the server node' do refute_match(/Error:.*i18ndemo/, result.stderr) end if agent == master step 'Expect the production environment, along with the module, to be synced back on the agent node' do assert_match(/Error:.*i18ndemo/, result.stderr) end if agent != master end on(agent, "ls #{custom_environment_path}") do |result| assert_match('production', result.stdout, "Default environment folder was not generated in last puppet run") end on(agent, "ls #{default_environment_path}") do |result| refute_match(/production/, result.stdout) end end step 'Set back to just default environmentpath setting' do on(agent, puppet("config delete environmentpath")) end step 'Expect production environment folder to be found in both paths but use the default one' do on(agent, puppet("agent -t"), :acceptable_exit_codes => [0, 2]) do |result| step 'Expect the module to be gone' do refute_match(/Error:.*i18ndemo/, result.stderr) end if agent == master end on(agent, "ls #{default_environment_path}") do |result| assert_match('production', result.stdout, "Default environment folder was not generated in last puppet run") end on(agent, "ls #{custom_environment_path}") do |result| assert_match('production', result.stdout) end end end use_agent_environment_when_enc_doesnt_specify.rb000066400000000000000000000041351470131746300351060ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/environmenttest_name "Agent should use agent environment if there is an enc that does not specify the environment" do require 'puppet/acceptance/classifier_utils' extend Puppet::Acceptance::ClassifierUtils tag 'audit:high', 'audit:integration', 'server' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end classify_nodes_as_agent_specified_if_classifer_present testdir = create_tmpdir_for_user(master, 'use_agent_env') create_remote_file(master, "#{testdir}/enc.rb", < true) File { ensure => directory, mode => "0770", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/more_different/':; '#{testdir}/environments/more_different/manifests':; } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, mode => "0640", content => 'notify { "production environment": }', } file { '#{testdir}/environments/more_different/manifests/more_different.pp': ensure => file, mode => "0640", content => 'notify { "more_different_string": }', } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", 'node_terminus' => 'exec', 'external_nodes' => "#{testdir}/enc.rb", }, } with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| run_agent_on(agent, "--no-daemonize --onetime --verbose --environment more_different") do |result| assert_match(/more_different_string/, result.stdout, "Did not find more_different_string from \"more_different\" environment") end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/use_agent_environment_when_no_enc.rb000066400000000000000000000035331470131746300325640ustar00rootroot00000000000000test_name "Agent should use agent environment if there is no enc-specified environment" do tag 'audit:high', 'audit:integration', 'audit:refactor', # This can be combined with use_agent_environment_when_enc_doesnt_specify test 'server' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end testdir = create_tmpdir_for_user(master, 'use_agent_env') apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, mode => "0770", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/more_different/':; '#{testdir}/environments/more_different/manifests':; } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, mode => "0640", content => 'notify { "production environment": }', } file { '#{testdir}/environments/more_different/manifests/more_different.pp': ensure => file, mode => "0640", content => 'notify { "more_different_string": }', } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", }, 'master' => { 'node_terminus' => 'plain' }, } with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| run_agent_on(agent, "--no-daemonize --onetime --verbose --environment more_different") do |result| assert_match(/more_different_string/, result.stdout, "Did not find more_different_string from \"more_different\" environment") end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/use_enc_environment.rb000066400000000000000000000050551470131746300276720ustar00rootroot00000000000000test_name 'Agent should use environment given by ENC and only compile a catalog once' do require 'puppet/acceptance/classifier_utils.rb' extend Puppet::Acceptance::ClassifierUtils tag 'audit:high', 'audit:integration', 'server' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end testdir = create_tmpdir_for_user(master, 'use_enc_env') if master.is_pe? group = { 'name' => 'Special Environment', 'description' => 'Classify our test agent nodes in the special environment.', 'environment' => 'special', 'environment_trumps' => true, } create_group_for_nodes(agents, group) else create_remote_file(master, "#{testdir}/enc.rb", < true) File { ensure => directory, mode => "0770", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/special/':; '#{testdir}/environments/special/manifests':; } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, mode => "0640", content => 'notify { "production environment": }', } file { '#{testdir}/environments/special/manifests/different.pp': ensure => file, mode => "0640", content => 'notify { "expected_string": }', } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", }, } master_opts['master'] = { 'node_terminus' => 'exec', 'external_nodes' => "#{testdir}/enc.rb", } if !master.is_pe? with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| run_agent_on(agent, "--no-daemonize --onetime --verbose") do |result| assert_match(/expected_string/, result.stdout, "Did not find expected_string from \"special\" environment") caching_catalog_message_count = result.stdout.split(/Info: Caching catalog for/).length - 1 assert_equal(caching_catalog_message_count, 1, 'Should only compile and cache the catalog once during the run') end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/use_enc_environment_for_files.rb000066400000000000000000000047441470131746300317260ustar00rootroot00000000000000test_name "Agent should use environment given by ENC for fetching remote files" do tag 'audit:high', 'audit:integration', 'audit:refactor', # This test should be rolled into use_enc_environment 'server' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end testdir = create_tmpdir_for_user(master, 'respect_enc_test') create_remote_file(master, "#{testdir}/enc.rb", < true) File { ensure => directory, mode => "0770", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/special/':; '#{testdir}/environments/special/manifests':; '#{testdir}/environments/special/modules':; '#{testdir}/environments/special/modules/amod':; '#{testdir}/environments/special/modules/amod/files':; } file { '#{testdir}/environments/special/modules/amod/files/testy': ensure => file, mode => "0640", content => 'special_environment', } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", 'environment_timeout' => 0, }, 'master' => { 'node_terminus' => 'exec', 'external_nodes' => "#{testdir}/enc.rb", }, } with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| atmp = agent.tmpdir('respect_enc_test') teardown do on(agent, "rm -rf '#{atmp}'") end logger.debug "agent: #{agent} \tagent.tmpdir => #{atmp}" create_remote_file(master, "#{testdir}/environments/special/manifests/different.pp", < "puppet:///modules/amod/testy", } END on(master, "chmod 644 '#{testdir}/environments/special/manifests/different.pp'") run_agent_on(agent, "--no-daemonize --onetime --verbose --trace") on(agent, "cat '#{atmp}/special_testy'") do |result| assert_match(/special_environment/, result.stdout, "The file from environment 'special' was not found") end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/use_enc_environment_for_pluginsync.rb000066400000000000000000000041141470131746300330060ustar00rootroot00000000000000test_name "Agent should use environment given by ENC for pluginsync" do tag 'audit:high', 'audit:integration', 'audit:refactor', # This test should be rolled into use_enc_environment 'server' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end testdir = create_tmpdir_for_user(master, 'respect_enc_test') create_remote_file(master, "#{testdir}/enc.rb", < true) File { ensure => directory, mode => "0770", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/special/':; '#{testdir}/environments/special/modules':; '#{testdir}/environments/special/modules/amod':; '#{testdir}/environments/special/modules/amod/lib':; '#{testdir}/environments/special/modules/amod/lib/puppet':; } file { '#{testdir}/environments/special/modules/amod/lib/puppet/foo.rb': ensure => file, mode => "0640", content => "#special_version", } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", }, 'master' => { 'node_terminus' => 'exec', 'external_nodes' => "#{testdir}/enc.rb" }, } with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| agent_vardir = agent.puppet['vardir'] teardown do on(agent, "rm -rf '#{agent_vardir}/lib'") end run_agent_on(agent, "-t --no-daemonize --onetime") on(agent, "cat '#{agent_vardir}/lib/puppet/foo.rb'") do |result| assert_match(/#special_version/, result.stdout, "The plugin from environment 'special' was not synced") end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/use_environment_from_environmentpath.rb000066400000000000000000000151751470131746300333750ustar00rootroot00000000000000test_name "Use environments from the environmentpath" do require 'puppet/acceptance/classifier_utils' extend Puppet::Acceptance::ClassifierUtils tag 'audit:high', 'audit:integration', 'server' classify_nodes_as_agent_specified_if_classifer_present testdir = create_tmpdir_for_user(master, 'use_environmentpath') def generate_environment(path_to_env, environment) <<-EOS "#{path_to_env}/#{environment}":; "#{path_to_env}/#{environment}/manifests":; "#{path_to_env}/#{environment}/modules":; EOS end def generate_module_content(module_name, options = {}) base_path = options[:base_path] environment = options[:environment] env_path = options[:env_path] path_to_module = [base_path, env_path, environment, "modules"].compact.join("/") module_info = "module-#{module_name}" module_info << "-from-#{environment}" if environment <<-EOS "#{path_to_module}/#{module_name}":; "#{path_to_module}/#{module_name}/manifests":; "#{path_to_module}/#{module_name}/files":; "#{path_to_module}/#{module_name}/templates":; "#{path_to_module}/#{module_name}/lib":; "#{path_to_module}/#{module_name}/lib/facter":; "#{path_to_module}/#{module_name}/manifests/init.pp": ensure => file, mode => "0640", content => 'class #{module_name} { notify { "template-#{module_name}": message => template("#{module_name}/our_template.erb") } file { "$agent_file_location/file-#{module_info}": source => "puppet:///modules/#{module_name}/data" } }' ; "#{path_to_module}/#{module_name}/lib/facter/environment_fact_#{module_name}.rb": ensure => file, mode => "0640", content => "Facter.add(:environment_fact_#{module_name}) { setcode { 'environment fact from #{module_info}' } }" ; "#{path_to_module}/#{module_name}/files/data": ensure => file, mode => "0640", content => "data file from #{module_info}" ; "#{path_to_module}/#{module_name}/templates/our_template.erb": ensure => file, mode => "0640", content => "<%= @environment_fact_#{module_name} %>" ; EOS end def generate_site_manifest(path_to_manifest, *modules_to_include) <<-EOS "#{path_to_manifest}/site.pp": ensure => file, mode => "0640", content => "#{modules_to_include.map {|m| "include #{m}"}.join("\n")}" ; EOS end apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, owner => #{master.puppet['user']}, group => #{master.puppet['group']}, mode => "0770", } file { "#{testdir}":; "#{testdir}/base":; "#{testdir}/additional":; "#{testdir}/modules":; #{generate_environment("#{testdir}/base", "shadowed")} #{generate_environment("#{testdir}/base", "onlybase")} #{generate_environment("#{testdir}/additional", "shadowed")} #{generate_module_content("atmp", :base_path => testdir, :env_path => 'base', :environment => 'shadowed')} #{generate_site_manifest("#{testdir}/base/shadowed/manifests", "atmp", "globalmod")} #{generate_module_content("atmp", :base_path => testdir, :env_path => 'base', :environment => 'onlybase')} #{generate_site_manifest("#{testdir}/base/onlybase/manifests", "atmp", "globalmod")} #{generate_module_content("atmp", :base_path => testdir, :env_path => 'additional', :environment => 'shadowed')} #{generate_site_manifest("#{testdir}/additional/shadowed/manifests", "atmp", "globalmod")} # And one global module (--modulepath setting) #{generate_module_content("globalmod", :base_path => testdir)} "#{testdir}/additional/production":; "#{testdir}/additional/production/manifests":; #{generate_site_manifest("#{testdir}/additional/production/manifests", "globalmod")} } MANIFEST def run_with_environment(agent, environment, options = {}) expected_exit_code = options[:expected_exit_code] || 2 step "running an agent in environment '#{environment}'" atmp = agent.tmpdir("use_environmentpath_#{environment}") teardown do on(agent, "rm -rf '#{atmp}'") end agent_config = [ "-t" ] agent_config << '--environment' << environment if environment # This to test how the agent behaves when using the directory environment # loaders (which will not load an environment if it does not exist) agent_config << "--environmentpath='$confdir/environments'" if agent != master agent_config << { 'ENV' => { "FACTER_agent_file_location" => atmp }, } on(agent, puppet("agent", *agent_config), :acceptable_exit_codes => [expected_exit_code]) do |result| yield atmp, result end end master_opts = { 'master' => { 'environmentpath' => "#{testdir}/additional:#{testdir}/base", 'basemodulepath' => "#{testdir}/modules", } } if master.is_pe? master_opts['master']['basemodulepath'] << ":#{master['sitemoduledir']}" end with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| run_with_environment(agent, "shadowed") do |tmpdir, catalog_result| ["module-atmp-from-shadowed", "module-globalmod"].each do |expected| assert_match(/environment fact from #{expected}/, catalog_result.stdout) end ["module-atmp-from-shadowed", "module-globalmod"].each do |expected| on(agent, "cat '#{tmpdir}/file-#{expected}'") do |file_result| assert_match(/data file from #{expected}/, file_result.stdout) end end end run_with_environment(agent, "onlybase") do |tmpdir, catalog_result| ["module-atmp-from-onlybase", "module-globalmod"].each do |expected| assert_match(/environment fact from #{expected}/, catalog_result.stdout) end ["module-atmp-from-onlybase", "module-globalmod"].each do |expected| on(agent, "cat '#{tmpdir}/file-#{expected}'") do |file_result| assert_match(/data file from #{expected}/, file_result.stdout) end end end run_with_environment(agent, "production", :expected_exit_code => 2) do |tmpdir, catalog_result| refute_match(/module-atmp/, catalog_result.stdout, "module-atmp was included despite the default environment being loaded") assert_match(/environment fact from module-globalmod/, catalog_result.stdout) on(agent, "cat '#{tmpdir}/file-module-globalmod'") do |file_result| assert_match(/data file from module-globalmod/, file_result.stdout) end end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/use_last_server_specified_environment.rb000066400000000000000000000055051470131746300334710ustar00rootroot00000000000000test_name "Agent should use the last server-specified environment if server is authoritative" do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'server' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end testdir = create_tmpdir_for_user(master, 'use_enc_env') create_remote_file(master, "#{testdir}/enc.rb", < true) File { ensure => directory, mode => "0770", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/special/':; '#{testdir}/environments/special/manifests':; } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, mode => "0640", content => 'notify { "production environment": }', } file { '#{testdir}/environments/special/manifests/different.pp': ensure => file, mode => "0640", content => 'notify { "special environment": }', } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", }, } master_opts['master'] = { 'node_terminus' => 'exec', 'external_nodes' => "#{testdir}/enc.rb", } if !master.is_pe? with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| step 'ensure the lastrunfile is absent for the first run' do on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end step 'first run: agent makes a node request to get the environment' do run_agent_on(agent, '--no-daemonize --onetime --debug') do |result| assert_match(/Local environment: 'production' doesn't match server specified node environment 'special', switching agent to 'special'/, result.stdout) assert_match(/Debug: HTTP GET .*\/puppet\/v3\/node/, result.stdout) assert_match(/Notice: special environment/, result.stdout) end end step 'second run: agent uses the environment from lastrunfile' do run_agent_on(agent, '--no-daemonize --onetime --debug') do |result| assert_match(/Debug: Successfully loaded last environment from the lastrunfile/, result.stdout) assert_match(/Notice: special environment/, result.stdout) end end end end end puppetlabs-puppet-789f600/acceptance/tests/environment/variables_refreshed_each_compilation.rb000066400000000000000000000103301470131746300331720ustar00rootroot00000000000000test_name 'C98115 compilation should get new values in variables on each compilation' do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils confine :except, :platform => /^(aix|osx|solaris)/ tag 'audit:high', 'audit:integration', 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" create_remote_file(master, "#{fq_tmp_environmentpath}/environment.conf", <<-CONF) environment_timeout = unlimited CONF # the module function loading logic is different from inside a single manifest # we exercise both here on(master, "mkdir -p '#{fq_tmp_environmentpath}'/modules/custom_time/{manifests,functions,facts.d}") create_remote_file(master, "#{fq_tmp_environmentpath}/modules/custom_time/manifests/init.pp", <<-FILE) class custom_time { $t = custom_time::my_system_time() notify { 'custom time': message => "module_${t}_module", } } FILE create_remote_file(master, "#{fq_tmp_environmentpath}/modules/custom_time/functions/my_system_time.pp", <<-FILE) function custom_time::my_system_time() { $facts['custom_time'] } FILE create_sitepp(master, tmp_environment, <<-SITE) function bar() { $facts['custom_time'] } class foo::bar { notify { "local_${bar()}_local": } } include foo::bar include custom_time SITE create_remote_file(master, "#{fq_tmp_environmentpath}/modules/custom_time/facts.d/custom_time.sh", <<-FILE) #!/bin/bash if [[ `uname` == 'Darwin' ]]; then echo -n "custom_time=$(date +%s)" else echo -n "custom_time=$(date +%s%N)" fi FILE on(master, "chmod -R 0777 '#{fq_tmp_environmentpath}/'") windows_fact_location = "#{fq_tmp_environmentpath}/modules/custom_time/facts.d/custom_time.ps1" create_remote_file(master, windows_fact_location, <<-FILE) echo "custom_time=$(get-date -format HHmmssffffff)" FILE on(master, "chmod -R 0666 '#{windows_fact_location}'") step "run agent in #{tmp_environment}, ensure it increments the customtime with each run" do with_puppet_running_on(master, {}) do local_custom_time_pattern = 'local_(\d+)_local' module_custom_time_pattern = 'module_(\d+)_module' agents.each do |agent| # ensure our custom facts have been synced on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :accept_all_exit_codes => true) local_custom_time1 = module_custom_time1 = nil local_custom_time2 = module_custom_time2 = nil on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :accept_all_exit_codes => [2]) do |result| assert_match(/Notice: #{local_custom_time_pattern}/, result.stdout, 'first custom time was not as expected') assert_match(/Notice: #{module_custom_time_pattern}/, result.stdout, 'first module uptime was not as expected') local_custom_time1 = result.stdout.match(/Notice: #{local_custom_time_pattern}/)[1].to_i module_custom_time1 = result.stdout.match(/Notice: #{module_custom_time_pattern}/)[1].to_i end sleep 1 on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :accept_all_exit_codes => [2]) do |result| assert_match(/Notice: #{local_custom_time_pattern}/, result.stdout, 'second custom time was not as expected') assert_match(/Notice: #{module_custom_time_pattern}/, result.stdout, 'second module uptime was not as expected') local_custom_time2 = result.stdout.match(/Notice: #{local_custom_time_pattern}/)[1].to_i module_custom_time2 = result.stdout.match(/Notice: #{module_custom_time_pattern}/)[1].to_i end assert(local_custom_time2 > local_custom_time1, 'local custom time did not change as expected if at all') assert(module_custom_time2 > module_custom_time1, 'module custom time did not change as expected if at all') end end end end puppetlabs-puppet-789f600/acceptance/tests/face/000077500000000000000000000000001470131746300216255ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/face/4654_facts_face.rb000066400000000000000000000035401470131746300247140ustar00rootroot00000000000000test_name "Puppet facts face should resolve custom and external facts" tag 'audit:high', 'audit:integration' # The facter acceptance tests should be acceptance. # However, the puppet face merely needs to interact with libfacter. # So, this should be an integration test. # # This test is intended to ensure that custom and external facts present # on the agent are resolved and displayed by the puppet facts face. # custom_fact = < < directory, } file { "#{agent.puppet['plugindest']}/facter/custom.rb": ensure => file, content => "#{custom_fact}", } file { "#{agent.puppet['pluginfactdest']}/external#{ext}": ensure => file, mode => "0755", content => "#{external_fact}", } MANIFEST step "Agent #{agent}: custom_fact and external_fact should be present in the output of `puppet facts`" on agent, puppet('facts') do |result| assert_match(/"custom_fact": "foo"/, result.stdout, "custom_fact did not match expected output") assert_match(/"external_fact": "bar"/, result.stdout, "external_fact did not match expected output") end end puppetlabs-puppet-789f600/acceptance/tests/face/loadable_from_modules.rb000066400000000000000000000077121470131746300264770ustar00rootroot00000000000000test_name "Exercise loading a face from a module" # Because the module tool does not work on windows, we can't run this test there confine :except, :platform => 'windows' tag 'audit:high', 'audit:acceptance', # This has been OS sensitive. 'audit:refactor' # Remove the confine against windows and refactor to # accommodate the Windows platform. require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs metadata_json_file = <<-FILE { "name": "puppetlabs-helloworld", "version": "0.0.1", "author": "Puppet Labs", "summary": "Nginx Module", "license": "Apache Version 2.0", "source": "https://github.com/puppetlabs/puppetlabs-nginx", "project_page": "https://github.com/puppetlabs/puppetlabs-nginx", "issues_url": "https://github.com/puppetlabs/puppetlabs-nginx", "dependencies": [ {"name":"puppetlabs-stdlub","version_requirement":">= 1.0.0"} ] } FILE agents.each do |agent| if on(agent, facter("fips_enabled")).stdout =~ /true/ puts "Module build, loading and installing not supported on fips enabled platforms" next end environmentpath = get_test_file_path(agent, 'environments') dev_modulepath = "#{environmentpath}/dev/modules" module_base_dir = "#{dev_modulepath}/helloworld" teardown do on agent, "rm -rf #{module_base_dir}" end # make sure that we use the modulepath from the dev environment puppetconf = get_test_file_path(agent, 'puppet.conf') on agent, puppet("config", "set", "environmentpath", environmentpath, "--section", "main", "--config", puppetconf) on agent, puppet("config", "set", "environment", "dev", "--section", "user", "--config", puppetconf) mkdirs agent, module_base_dir create_remote_file(agent, "#{module_base_dir}/metadata.json", metadata_json_file) mkdirs agent, "#{module_base_dir}/lib/puppet/application" mkdirs agent, "#{module_base_dir}/lib/puppet/face" # copy application, face, and utility module create_remote_file(agent, "#{module_base_dir}/lib/puppet/application/helloworld.rb", <<'EOM') require 'puppet/face' require 'puppet/application/face_base' class Puppet::Application::Helloworld < Puppet::Application::FaceBase end EOM create_remote_file(agent, "#{module_base_dir}/lib/puppet/face/helloworld.rb", <<'EOM') Puppet::Face.define(:helloworld, '0.1.0') do summary "Hello world face" description "This is the hello world face" action 'actionprint' do summary "Prints hello world from an action" when_invoked do |options| puts "Hello world from an action" end end action 'moduleprint' do summary "Prints hello world from a required module" when_invoked do |options| require 'puppet/helloworld.rb' Puppet::Helloworld.print end end end EOM create_remote_file(agent, "#{module_base_dir}/lib/puppet/helloworld.rb", <<'EOM') module Puppet::Helloworld def print puts "Hello world from a required module" end module_function :print end EOM on(agent, puppet('help', '--config', puppetconf)) do |result| assert_match(/helloworld\s*Hello world face/, result.stdout, "Face missing from list of available subcommands") end on(agent, puppet('help', 'helloworld', '--config', puppetconf)) do |result| assert_match(/This is the hello world face/, result.stdout, "Descripion help missing") assert_match(/moduleprint\s*Prints hello world from a required module/, result.stdout, "help for moduleprint action missing") assert_match(/actionprint\s*Prints hello world from an action/, result.stdout, "help for actionprint action missing") end on(agent, puppet('helloworld', 'actionprint', '--config', puppetconf)) do |result| assert_match(/^Hello world from an action$/, result.stdout, "face did not print hello world") end on(agent, puppet('helloworld', 'moduleprint', '--config', puppetconf)) do |result| assert_match(/^Hello world from a required module$/, result.stdout, "face did not load module to print hello world") end end puppetlabs-puppet-789f600/acceptance/tests/face/parser_validate.rb000066400000000000000000000053141470131746300253220ustar00rootroot00000000000000test_name 'parser validate' do tag 'audit:high', 'audit:unit' # Parser validation should be core to ruby # and platform agnostic. require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils app_type = File.basename(__FILE__, '.*') teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end agents.each do |agent| skip_test('this test fails on windows French due to Cygwin/UTF Issues - PUP-8319,IMAGES-492') if agent['platform'] =~ /windows/ && agent['locale'] == 'fr' step 'manifest with parser function call' do if agent.platform !~ /windows/ tmp_environment = mk_tmp_environment_with_teardown(agent, app_type) create_sitepp(agent, tmp_environment, <<-SITE) function validate_this() { notice('hello, puppet') } validate_this() SITE on(agent, puppet("parser validate --environment #{tmp_environment}"), :pty => true) # default manifest end # manifest with Type aliases create_test_file(agent, "#{app_type}.pp", <<-PP) function validate_this() { notice('hello, puppet') } validate_this() type MyInteger = Integer notice 42 =~ MyInteger PP tmp_manifest = get_test_file_path(agent, "#{app_type}.pp") on(agent, puppet("parser validate #{tmp_manifest}")) end step 'manifest with bad syntax' do create_test_file(agent, "#{app_type}_broken.pp", "notify 'hello there'") tmp_manifest = get_test_file_path(agent, "#{app_type}_broken.pp") on(agent, puppet("parser validate #{tmp_manifest}"), :accept_all_exit_codes => true) do |result| assert_equal(result.exit_code, 1, 'parser validate did not exit with 1 upon parse failure') expected = /Error: Could not parse for environment production: This Name has no effect\. A value was produced and then forgotten \(one or more preceding expressions may have the wrong form\) \(file: .*_broken\.pp, line: 1, column: 1\)/ assert_match(expected, result.output, "parser validate did not output correctly: '#{result.output}'. expected: '#{expected.to_s}'") unless agent['locale'] == 'ja' end end step '(large) manifest with exported resources' do fixture_path = File.join(File.dirname(__FILE__), '..', '..', 'fixtures/manifest_large_exported_classes_node.pp') create_test_file(agent, "#{app_type}_exported.pp", File.read(fixture_path)) tmp_manifest = get_test_file_path(agent, "#{app_type}_exported.pp") on(agent, puppet("parser validate #{tmp_manifest}")) end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/000077500000000000000000000000001470131746300215065ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/i18n/enable_option_disable_i18n.rb000066400000000000000000000060231470131746300271740ustar00rootroot00000000000000test_name 'Verify that disable_i18n can be set to true and have translations disabled' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' step "configure server locale to #{language}" do configure_master_system_locale(language) end tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*')) step 'install a i18ndemo module' do install_i18n_demo_module(master, tmp_environment) end disable_i18n_default_master = master.puppet['disable_i18n'] teardown do step 'resetting the server locale' do on(master, puppet("config set disable_i18n #{ disable_i18n_default_master }")) reset_master_system_locale end step 'uninstall the module' do agents.each do |agent| uninstall_i18n_demo_module(agent) end uninstall_i18n_demo_module(master) end end agents.each do |agent| agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) on(master, puppet("config set disable_i18n false")) reset_master_system_locale end step 'expect #{language} translation for a custom type' do site_pp_content = <<-PP node default { i18ndemo_type { '12345': } } PP create_sitepp(master, tmp_environment, site_pp_content) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result| assert_match(/Error: .* \w+-i18ndemo type: 値は有12345効な値ではありません/, result.stderr, 'missing error from invalid value for custom type param') end end step 'disable i18n' do on(agent, puppet("config set disable_i18n true")) on(master, puppet("config set disable_i18n true")) reset_master_system_locale end step 'expect no #{language} translation for a custom type' do site_pp_content = <<-PP node default { i18ndemo_type { '12345': } } PP create_sitepp(master, tmp_environment, site_pp_content) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result| assert_match(/Error: .* Value 12345 is not a valid value for i18ndemo_type\:\:name/, result.stderr) end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/000077500000000000000000000000001470131746300231565ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_agent.rb000066400000000000000000000126441470131746300262050ustar00rootroot00000000000000test_name 'C100565: puppet agent with module should translate messages' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' skip_test('i18n test module uses deprecated function; update module to resume testing.') # function validate_absolute_path used https://github.com/eputnam/eputnam-i18ndemo/blob/621d06d/manifests/init.pp#L15 require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' disable_i18n_default_master = master.puppet['disable_i18n'] step 'enable i18n on master' do on(master, puppet("config set disable_i18n false")) end step "configure server locale to #{language}" do configure_master_system_locale(language) end tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*')) step 'install a i18ndemo module' do install_i18n_demo_module(master, tmp_environment) end teardown do step 'resetting the server locale' do on(master, puppet("config set disable_i18n #{ disable_i18n_default_master }")) reset_master_system_locale end step 'uninstall the module' do agents.each do |agent| uninstall_i18n_demo_module(agent) end uninstall_i18n_demo_module(master) end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end agents.each do |agent| skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja' agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } type_path = agent.tmpdir('provider') disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) agent.rm_rf(type_path) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end step "Run puppet agent of a module with language #{agent_language} and verify the translations" do step 'verify custom fact translations' do site_pp_content_1 = <<-PP node default { class { 'i18ndemo': filename => '#{type_path}' } } PP create_sitepp(master, tmp_environment, site_pp_content_1) on(agent, puppet("agent -t --no-disable_i18n --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/.*\w+-i18ndemo fact: これは\w+-i18ndemoからのカスタムファクトからのレイズです/, result.stderr, 'missing translation for raise from ruby fact') end end unless agent['platform'] =~ /ubuntu-16.04/ # Condition to be removed after FACT-2799 gets resolved step 'verify custom type translations' do site_pp_content_2 = <<-PP node default { i18ndemo_type { 'hello': } } PP create_sitepp(master, tmp_environment, site_pp_content_2) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result| assert_match(/Warning:.*\w+-i18ndemo type: 良い値/, result.stderr, 'missing warning from custom type') end site_pp_content_3 = <<-PP node default { i18ndemo_type { '12345': } } PP create_sitepp(master, tmp_environment, site_pp_content_3) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result| assert_match(/Error: .* \w+-i18ndemo type: 値は有12345効な値ではありません/, result.stderr, 'missing error from invalid value for custom type param') end end step 'verify custom provider translation' do site_pp_content_4 = <<-PP node default { i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', } } PP create_sitepp(master, tmp_environment, site_pp_content_4) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language)) do |result| assert_match(/Warning:.*\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, result.stderr, 'missing translated provider message') end end step 'verify function string translation' do site_pp_content_5 = <<-PP node default { notify { 'happy': message => happyfuntime('happy') } } PP create_sitepp(master, tmp_environment, site_pp_content_5) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => 2) do |result| assert_match(/Notice: --\*\w+-i18ndemo function: それは楽しい時間です\*--/, result.stdout, 'missing translated notice message') end end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_agent_cached_catalog.rb000066400000000000000000000125651470131746300311700ustar00rootroot00000000000000test_name 'C100566: puppet agent with module should translate messages when using a cached catalog' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' disable_i18n_default_master = master.puppet['disable_i18n'] step 'enable i18n on master' do on(master, puppet("config set disable_i18n false")) end step "configure server locale to #{language}" do configure_master_system_locale(language) end tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*')) step 'install a i18ndemo module' do install_i18n_demo_module(master, tmp_environment) end teardown do step 'resetting the server locale' do on(master, puppet("config set disable_i18n #{ disable_i18n_default_master }")) reset_master_system_locale end step 'uninstall the module' do agents.each do |agent| uninstall_i18n_demo_module(agent) end uninstall_i18n_demo_module(master) end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end agents.each do |agent| skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja' agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } type_path = agent.tmpdir('provider') disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) agent.rm_rf(type_path) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end unresolved_server = 'puppet.unresolved.host.example.com' step "Run puppet apply of a module with language #{agent_language} and verify the translations using the cached catalog" do step 'verify custom fact translations' do site_pp_content_1 = <<-PP node default { class { 'i18ndemo': filename => '#{type_path}' } } PP create_sitepp(master, tmp_environment, site_pp_content_1) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/.*\w+-i18ndemo fact: これは\w+-i18ndemoからのカスタムファクトからのレイズです/, result.stderr, 'missing translation for raise from ruby fact') end on(agent, puppet("agent -t --environment #{tmp_environment} --use_cached_catalog", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/.*\w+-i18ndemo fact: これは\w+-i18ndemoからのカスタムファクトからのレイズです/, result.stderr, 'missing translation for raise from ruby fact when using cached catalog') end end step 'verify custom provider translation' do site_pp_content_2 = <<-PP node default { i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', } } PP create_sitepp(master, tmp_environment, site_pp_content_2) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/Warning:.*\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, result.stderr, 'missing translated provider message') end on(agent, puppet("agent -t --server #{unresolved_server} --environment #{tmp_environment} --use_cached_catalog", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/Warning:.*\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, result.stderr, 'missing translated provider message when using cached catalog') end end step 'verify function string translation' do site_pp_content_3 = <<-PP node default { notify { 'happy': message => happyfuntime('happy') } } PP create_sitepp(master, tmp_environment, site_pp_content_3) on(agent, puppet("agent -t --environment #{tmp_environment}", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/Notice: --\*\w+-i18ndemo function: それは楽しい時間です\*--/, result.stdout, 'missing translated notice message') end on(agent, puppet("agent -t --server #{unresolved_server} --environment #{tmp_environment} --use_cached_catalog", 'ENV' => shell_env_language), :acceptable_exit_codes => [0, 2]) do |result| assert_match(/Notice: --\*\w+-i18ndemo function: それは楽しい時間です\*--/, result.stdout, 'missing translated notice message when using cached catalog') end end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_agent_with_multiple_environments.rb000066400000000000000000000102421470131746300337520ustar00rootroot00000000000000test_name 'C100575: puppet agent with different modules in different environments should translate based on their module' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' app_type_1 = File.basename(__FILE__, '.*') + "_env_1" app_type_2 = File.basename(__FILE__, '.*') + "_env_2" tmp_environment_1 = mk_tmp_environment_with_teardown(master, app_type_1) tmp_environment_2 = mk_tmp_environment_with_teardown(master, app_type_2) full_path_env_1 = File.join('/tmp', tmp_environment_1) full_path_env_2 = File.join('/tmp', tmp_environment_2) tmp_po_file = master.tmpfile('tmp_po_file') disable_i18n_default_master = master.puppet['disable_i18n'] step 'enable i18n on master' do on(master, puppet("config set disable_i18n false")) end step 'install a i18ndemo module' do install_i18n_demo_module(master, tmp_environment_1) install_i18n_demo_module(master, tmp_environment_2) end step "configure server locale to #{language}" do configure_master_system_locale(language) end teardown do on(master, "rm -f '#{tmp_po_file}'") step 'uninstall the module' do agents.each do |agent| uninstall_i18n_demo_module(agent) end uninstall_i18n_demo_module(master) end step 'resetting the server locale' do on(master, puppet("config set disable_i18n #{ disable_i18n_default_master }")) reset_master_system_locale end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end agents.each do |agent| skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja' agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end env_1_po_file = File.join(full_path_env_1, 'modules', I18NDEMO_NAME, 'locales', 'ja', "#{I18NDEMO_MODULE_NAME}.po") on(master, "sed -e 's/\\(msgstr \"\\)\\([^\"]\\)/\\1'\"ENV_1\"':\\2/' #{env_1_po_file} > #{tmp_po_file} && mv #{tmp_po_file} #{env_1_po_file}") env_2_po_file = File.join(full_path_env_2, 'modules', I18NDEMO_NAME, 'locales', 'ja', "#{I18NDEMO_MODULE_NAME}.po") on(master, "sed -e 's/\\(msgstr \"\\)\\([^\"]\\)/\\1'\"ENV_2\"':\\2/' #{env_2_po_file} > #{tmp_po_file} && mv #{tmp_po_file} #{env_2_po_file}") on(master, "chmod a+r '#{env_1_po_file}' '#{env_2_po_file}'") step 'verify function string translation' do site_pp_content = <<-PP node default { notify { 'happy': message => happyfuntime('happy') } } PP create_sitepp(master, tmp_environment_1, site_pp_content) on(agent, puppet("agent -t --environment #{tmp_environment_1}", 'ENV' => shell_env_language), :acceptable_exit_codes => 2) do |result| assert_match(/Notice: --\*(ENV_1:)?ENV_1:\w+-i18ndemo function: それは楽しい時間です\*--/, result.stdout, 'missing translated notice message for environment 1') end create_sitepp(master, tmp_environment_2, site_pp_content) on(agent, puppet("agent -t --environment #{tmp_environment_2}", 'ENV' => shell_env_language), :acceptable_exit_codes => 2) do |result| assert_match(/Notice: --\*(ENV_2:)?ENV_2:\w+-i18ndemo function: それは楽しい時間です\*--/, result.stdout, 'missing translated notice message for environment 2') end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_apply.rb000066400000000000000000000073751470131746300262410ustar00rootroot00000000000000test_name 'C100567: puppet apply of module should translate messages' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' agents.each do |agent| skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja' # REMIND - It was noted that skipping tests on certain platforms sometimes causes # beaker to mark the test as a failed even if the test succeeds on other targets. # Hence we just print a message and skip w/o telling beaker about it. if on(agent, facter("fips_enabled")).stdout =~ /true/ puts "Module build, loading and installing is not supported on fips enabled platforms" next end agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } type_path = agent.tmpdir('provider') step 'install a i18ndemo module' do install_i18n_demo_module(agent) end disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) uninstall_i18n_demo_module(agent) on(agent, "rm -rf '#{type_path}'") end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end step "Run puppet apply of a module with language #{agent_language} and verify the translations" do step 'verify custom fact translations' do on(agent, puppet("apply -e \"class { 'i18ndemo': filename => '#{type_path}' }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/.*\w+-i18ndemo fact: これは\w+-i18ndemoからのカスタムファクトからのレイズです/, apply_result.stderr, 'missing translation for raise from ruby fact') end end unless agent['platform'] =~ /ubuntu-16.04/ # Condition to be removed after FACT-2799 gets resolved step 'verify custom translations' do on(agent, puppet("apply -e \"i18ndemo_type { 'hello': }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Warning:.*\w+-i18ndemo type: 良い値/, apply_result.stderr, 'missing warning from custom type') end on(agent, puppet("apply -e \"i18ndemo_type { '12345': }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Error: .* \w+-i18ndemo type: 値は有12345効な値ではありません/, apply_result.stderr, 'missing error from invalid value for custom type param') end end step 'verify custom provider translation' do on(agent, puppet("apply -e \"i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Warning:.*\w+-i18ndemo provider: i18ndemo_typeは存在しますか/, apply_result.stderr, 'missing translated provider message') end end step 'verify function string translation' do on(agent, puppet("apply -e \"notify { 'happy': message => happyfuntime('happy') }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Notice: --\*\w+-i18ndemo function: それは楽しい時間です\*--/, apply_result.stdout, 'missing translated notice message') end end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_apply_module_lang.rb000066400000000000000000000107361470131746300306020ustar00rootroot00000000000000test_name 'C100574: puppet apply using a module should translate messages in a language not supported by puppet' do confine :except, :platform => /^windows/ # Can't print Finish on an English or Japanese code page tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language='fi_FI' agents.each do |agent| # REMIND - It was noted that skipping tests on certain platforms sometimes causes # beaker to mark the test as a failed even if the test succeeds on other targets. # Hence we just print a message and skip w/o telling beaker about it. if on(agent, facter("fips_enabled")).stdout =~ /true/ puts "Module build, loading and installing is not supported on fips enabled platforms" next end agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } type_path = agent.tmpdir('provider') disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) uninstall_i18n_demo_module(agent) on(agent, "rm -rf '#{type_path}'") end step 'install a i18ndemo module' do install_i18n_demo_module(agent) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end step "Run puppet apply of a module with language #{agent_language} and verify default english returned" do step 'verify custom fact message translated and applied catalog message not translatated' do on(agent, puppet("apply -e \"class { 'i18ndemo': filename => '#{type_path}' }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/i18ndemo_fact: tämä on korotus mukautetusta tosiasiasta \w+-i18ndemo/, apply_result.stderr, 'missing translated message for raise from ruby fact') assert_match(/Notice: Applied catalog in [0-9.]+ seconds/, apply_result.stdout, 'missing untranslated message for catalog applied') end end unless agent['platform'] =~ /ubuntu-16.04/ # Condition to be removed after FACT-2799 gets resolved step 'verify warning translated from init.pp' do on(agent, puppet("apply -e \"class { 'i18ndemo': filename => '#{type_path}' }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Warning: .*I18ndemo-tiedoston luominen/, apply_result.stderr, 'missing translated warning from init.pp') end on(agent, puppet("apply -e \"class { 'i18ndemo': param1 => false }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Error: .* tiedostoa ei voitu luoda./, apply_result.stderr, 'missing translated message for fail from init.pp') end end step 'verify custom type messages translated' do on(agent, puppet("apply -e \"i18ndemo_type { 'hello': }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Warning: .* Hyvä arvo i18ndemo_type::name/, apply_result.stderr, 'missing translated warning from custom type') end on(agent, puppet("apply -e \"i18ndemo_type { '12345': }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Error: .* Arvo 12345 ei ole kelvollinen arvo i18ndemo_type::name/, apply_result.stderr, 'missing translated error from invalid value for custom type param') end end step 'verify custom provider translation' do on(agent, puppet("apply -e \"i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Warning: .* Onko i18ndemo_type olemassa\?/, apply_result.stderr, 'missing translated provider message') end end step 'verify function string translation' do on(agent, puppet("apply -e \"notify { 'happy': message => happyfuntime('happy') }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Notice: --\*SE ON HAUSKAA AIKAA\*--/, apply_result.stdout, 'missing translated notice message') end end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_apply_unsupported_lang.rb000066400000000000000000000075551470131746300317120ustar00rootroot00000000000000test_name 'C100568: puppet apply of module for an unsupported language should fall back to english' do tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils unsupported_language='hu_HU' shell_env_language = { 'LANGUAGE' => unsupported_language, 'LANG' => unsupported_language } agents.each do |agent| # REMIND - It was noted that skipping tests on certain platforms sometimes causes # beaker to mark the test as a failed even if the test succeeds on other targets. # Hence we just print a message and skip w/o telling beaker about it. if on(agent, facter("fips_enabled")).stdout =~ /true/ puts "Module build, loading and installing is not supported on fips enabled platforms" next end type_path = agent.tmpdir('provider') disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) uninstall_i18n_demo_module(agent) on(agent, "rm -rf '#{type_path}'") end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end step 'install a i18ndemo module' do install_i18n_demo_module(agent) end step "Run puppet apply of a module with language #{unsupported_language} and verify default english returned" do step 'verify custom fact messages not translatated' do on(agent, puppet("apply -e \"class { 'i18ndemo': filename => '#{type_path}' }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/.*i18ndemo_fact: this is a raise from a custom fact from \w+-i18ndemo/, apply_result.stderr, 'missing untranslated message for raise from ruby fact') end end step 'verify warning not translated from init.pp' do on(agent, puppet("apply -e \"class { 'i18ndemo': filename => '#{type_path}' }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Warning:.*Creating an i18ndemo file/, apply_result.stderr, 'missing untranslated warning from init.pp') end on(agent, puppet("apply -e \"class { 'i18ndemo': param1 => false }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Error:.*Failed to create/, apply_result.stderr, 'missing untranslated message for fail from init.pp') end end step 'verify custom type messages not translated' do on(agent, puppet("apply -e \"i18ndemo_type { 'hello': }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Warning:.*Good value for i18ndemo_type::name/, apply_result.stderr, 'missing untranslated warning from custom type') end on(agent, puppet("apply -e \"i18ndemo_type { '12345': }\"", 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |apply_result| assert_match(/Error:.*Value 12345 is not a valid value for i18ndemo_type::name/, apply_result.stderr, 'missing untranslated error from invalid value for custom type param') end end step 'verify custom provider translation' do on(agent, puppet("apply -e \"i18ndemo_type { 'hello': ensure => present, dir => '#{type_path}', }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Warning:.* Does i18ndemo_type exist\?/, apply_result.stderr, 'missing untranslated provider message') end end step 'verify function string translation' do on(agent, puppet("apply -e \"notify { 'happy': message => happyfuntime('happy') }\"", 'ENV' => shell_env_language)) do |apply_result| assert_match(/Notice: --\*IT'S HAPPY FUN TIME\*--/, apply_result.stdout, 'missing untranslated notice message') end end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_describe.rb000066400000000000000000000043031470131746300266600ustar00rootroot00000000000000test_name 'C100576: puppet describe with module type translates message' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' agents.each do |agent| skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja' # REMIND - It was noted that skipping tests on certain platforms sometimes causes # beaker to mark the test as a failed even if the test succeeds on other targets. # Hence we just print a message and skip w/o telling beaker about it. if on(agent, facter("fips_enabled")).stdout =~ /true/ puts "Module build, loading and installing is not supported on fips enabled platforms" next end agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) uninstall_i18n_demo_module(agent) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end step 'install a i18ndemo module' do install_i18n_demo_module(agent) end step "Run puppet describe from a module with language #{agent_language} and verify the translations" do on(agent, puppet('describe i18ndemo_type', 'ENV' => shell_env_language)) do |result| assert_match(/\w+-i18ndemo type: dirパラメータは、検査するディレクトリパスをとります/, result.stdout, 'missing translation of dir parameter from i18ndemo_type') assert_match(/\w+-i18ndemo type: nameパラメータには、大文字と小文字の変数A-Za-zが使用されます/, result.stdout, 'missing translation of name parameter from i18ndemo_type') end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_face.rb000066400000000000000000000053561470131746300260070ustar00rootroot00000000000000test_name 'C100573: puppet application/face with module translates messages' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' agents.each do |agent| skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja' # REMIND - It was noted that skipping tests on certain platforms sometimes causes # beaker to mark the test as a failed even if the test succeeds on other targets. # Hence we just print a message and skip w/o telling beaker about it. if on(agent, facter("fips_enabled")).stdout =~ /true/ puts "Module build, loading and installing is not supported on fips enabled platforms" next end agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) uninstall_i18n_demo_module(agent) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end step 'install a i18ndemo module' do install_i18n_demo_module(agent) end step "Run puppet i18ndemo (face/application from a module with language #{agent_language} and verify the translations" do step 'puppet --help contains i18ndemo summary translation' do on(agent, puppet('--help', 'ENV' => shell_env_language)) do |result| assert_match(/\s*i18ndemo\s+\w+-i18ndemo face: I18ndemoモジュールの人形の顔の例/, result.stdout, 'missing translation of i18ndemo help summary') end end step 'puppet i18ndemo --help contains test_face summary' do on(agent, puppet('i18ndemo --help', 'ENV' => shell_env_language)) do |result| assert_match(/\s*test_face\s+\w+-i18ndemo face: test_faceアクションのヘルプの要約/, result.stdout, 'missing translation of i18ndemo face help summary') end end step 'puppet i18ndemo test_face contains translated warning' do on(agent, puppet('i18ndemo', 'ENV' => shell_env_language)) do |result| assert_match(/Warning: \w+-i18ndemo face: i18ndemo test_faceが呼び出されました/, result.stderr, 'missing translation of Warning message from i18ndemo face') end end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/modules/puppet_resource.rb000066400000000000000000000042041470131746300267270ustar00rootroot00000000000000test_name 'C100572: puppet resource with module translates messages' do confine :except, :platform => /^solaris/ # translation not supported tag 'audit:medium', 'audit:acceptance' require 'puppet/acceptance/i18n_utils' extend Puppet::Acceptance::I18nUtils require 'puppet/acceptance/i18ndemo_utils' extend Puppet::Acceptance::I18nDemoUtils language = 'ja_JP' agents.each do |agent| skip_test('on windows this test only works on a machine with a japanese code page set') if agent['platform'] =~ /windows/ && agent['locale'] != 'ja' # REMIND - It was noted that skipping tests on certain platforms sometimes causes # beaker to mark the test as a failed even if the test succeeds on other targets. # Hence we just print a message and skip w/o telling beaker about it. if on(agent, facter("fips_enabled")).stdout =~ /true/ puts "Module build, loading and installing is not supported on fips enabled platforms" next end agent_language = enable_locale_language(agent, language) skip_test("test machine is missing #{agent_language} locale. Skipping") if agent_language.nil? shell_env_language = { 'LANGUAGE' => agent_language, 'LANG' => agent_language } disable_i18n_default_agent = agent.puppet['disable_i18n'] teardown do on(agent, puppet("config set disable_i18n #{ disable_i18n_default_agent }")) uninstall_i18n_demo_module(agent) end step 'enable i18n' do on(agent, puppet("config set disable_i18n false")) end step 'install a i18ndemo module' do install_i18n_demo_module(agent) end step "Run puppet resource for a module with language #{agent_language} and verify the translations" do step 'puppet resource i18ndemo_type information contains translation' do on(agent, puppet('resource i18ndemo_type', 'ENV' => shell_env_language), :acceptable_exit_codes => 1) do |result| assert_match(/Warning: Puppet::Type::I18ndemo_type::ProviderRuby: \w+-i18ndemo type: i18ndemo_typeからの警告メッセージ/, result.stderr, 'missing translation of resource i18ndemo_type information') end end end end end puppetlabs-puppet-789f600/acceptance/tests/i18n/translation_fallback.rb000066400000000000000000000015301470131746300262070ustar00rootroot00000000000000test_name 'C100560: puppet agent run output falls back to english when language not available' do # No confines because even on non-translation supported OS' we should still fall back to english tag 'audit:medium', 'audit:acceptance' agents.each do |agent| step 'Run Puppet apply with language Hungarian and check the output' do unsupported_language='hu_HU' on(agent, puppet("agent -t", 'ENV' => {'LANG' => unsupported_language, 'LANGUAGE' => ''})) do |apply_result| assert_match(/Applying configuration version '[^']*'/, apply_result.stdout, 'agent run should default to english translation') assert_match(/Applied catalog in [0-9.]* seconds/, apply_result.stdout, 'agent run should default to english translation') end end end end puppetlabs-puppet-789f600/acceptance/tests/language/000077500000000000000000000000001470131746300225125ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/language/binary_data_type.rb000066400000000000000000000113521470131746300263570ustar00rootroot00000000000000test_name 'C98346: Binary data type' do require 'puppet/acceptance/puppet_type_test_tools.rb' extend Puppet::Acceptance::PuppetTypeTestTools tag 'audit:high', 'audit:integration', # Tests that binary data is retains integrity # between server and agent transport/application. # The weak link here is final ruby translation and # should not be OS sensitive. 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) tmp_filename_win = tmp_filename_else = '' agents.each do |agent| # ugh... this won't work with more than two agents of two types if agent.platform =~ /32$/ tmp_filename_win = "C:\\cygwin\\tmp\\#{tmp_environment}.txt" else tmp_filename_win = "C:\\cygwin64\\tmp\\#{tmp_environment}.txt" end tmp_filename_else = "/tmp/#{tmp_environment}.txt" on(agent, "echo 'old content' > '/tmp/#{tmp_environment}.txt'") end # create a fake module files... file for binary_file() on(master, puppet_apply("-e 'file{[\"#{environmentpath}/#{tmp_environment}/modules\",\"#{environmentpath}/#{tmp_environment}/modules/empty\",\"#{environmentpath}/#{tmp_environment}/modules/empty/files\"]: ensure => \"directory\"} file{\"#{environmentpath}/#{tmp_environment}/modules/empty/files/blah.txt\": content => \"binary, yo\"}'")) base64_relaxed = Base64.encode64("invasionfromspace#{random_string}").strip base64_strict = Base64.strict_encode64("invasion from space #{random_string}\n") base64_urlsafe = Base64.urlsafe_encode64("invasion from-space/#{random_string}\n") test_resources = [ { :type => 'notify', :parameters => { :namevar => "1:$hell" }, :pre_code => "$hell = Binary('hello','%b')", :assertions => { :assert_match => 'Notice: 1:hell' } }, { :type => 'notify', :parameters => { :namevar => "2:$relaxed" }, :pre_code => "$relaxed = Binary('#{base64_relaxed}')", :assertions => { :assert_match => "Notice: 2:#{base64_relaxed}" } }, { :type => 'notify', :parameters => { :namevar => "3:$cHVwcGV0" }, :pre_code => "$cHVwcGV0 = Binary('cHVwcGV0')", :assertions => { :assert_match => 'Notice: 3:cHVwcGV0' } }, { :type => 'notify', :parameters => { :namevar => "4:$strict" }, :pre_code => "$strict = Binary('#{base64_strict}')", :assertions => { :assert_match => "Notice: 4:#{base64_strict}" } }, { :type => 'notify', :parameters => { :namevar => "5:$urlsafe" }, :pre_code => "$urlsafe = Binary('#{base64_urlsafe}')", :assertions => { :assert_match => "Notice: 5:#{base64_urlsafe}" } }, { :type => 'notify', :parameters => { :namevar => "6:$byte_array" }, :pre_code => "$byte_array = Binary([67,68])", :assertions => { :assert_match => "Notice: 6:Q0Q=" } }, { :type => 'notify', :parameters => { :namevar => "7:${empty_array}empty" }, :pre_code => "$empty_array = Binary([])", :assertions => { :assert_match => "Notice: 7:empty" } }, { :type => 'notify', :parameters => { :namevar => "8:${relaxed[1]}" }, :assertions => { :assert_match => "Notice: 8:bg==" } }, { :type => 'notify', :parameters => { :namevar => "9:${relaxed[1,3]}" }, :assertions => { :assert_match => "Notice: 9:bnZh" } }, { :type => 'notify', :parameters => { :namevar => "A:${utf8}" }, :pre_code => '$utf8=String(Binary([0xF0, 0x9F, 0x91, 0x92]),"%s")', :assertions => { :assert_match => 'Notice: A:\\xF0\\x9F\\x91\\x92' } }, { :type => 'notify', :parameters => { :namevar => "B:${type($bin_file)}" }, :pre_code => '$bin_file=binary_file("empty/blah.txt")', :assertions => { :assert_match => 'Notice: B:Binary' } }, { :type => 'file', :parameters => { :namevar => "$pup_tmp_filename", :content => "$relaxed" }, :pre_code => "$pup_tmp_filename = if $facts['os']['family'] == 'windows' { '#{tmp_filename_win}' } else { '#{tmp_filename_else}' }", :assertions => { :assert_match => /#{base64_relaxed}/ } }, ] sitepp_content = generate_manifest(test_resources) assertion_code = generate_assertions(test_resources) create_sitepp(master, tmp_environment, sitepp_content) step "run agent in #{tmp_environment}, run all assertions" do with_puppet_running_on(master, {}) do agents.each do |agent| on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => [2]) do |result| run_assertions(assertion_code, result) end end end end end puppetlabs-puppet-789f600/acceptance/tests/language/exported_resources.rb000066400000000000000000000163311470131746300267670ustar00rootroot00000000000000test_name "C94788: exported resources using a yaml terminus for storeconfigs" do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:integration', 'audit:refactor', # This could be a component of a larger workflow scenario. 'server' skip_test 'requires puppetserver to service restart' if @options[:type] != 'aio' app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) exported_username = 'er0ck' teardown do step 'stop puppet server' do on(master, "service #{master['puppetservice']} stop") end step 'remove cached agent json catalogs from the master' do on(master, "rm -f #{File.join(master.puppet['yamldir'],'catalog','*')}", :accept_all_exit_codes => true) end on(master, "mv #{File.join('','tmp','puppet.conf')} #{master.puppet['confdir']}", :accept_all_exit_codes => true) step 'clean out collected resources' do on(hosts, puppet_resource("user #{exported_username} ensure=absent"), :accept_all_exit_codes => true) end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end storeconfigs_backend_name = 'json_storeconfigs' step 'create a yaml storeconfigs terminus in the modulepath' do moduledir = File.join(environmentpath,tmp_environment,'modules') terminus_class_name = 'JsonStoreconfigs' manifest = < directory, } file { '#{moduledir}':; '#{moduledir}/yaml_terminus':; '#{moduledir}/yaml_terminus/lib':; '#{moduledir}/yaml_terminus/lib/puppet':; '#{moduledir}/yaml_terminus/lib/puppet/indirector':; '#{moduledir}/yaml_terminus/lib/puppet/indirector/catalog':; '#{moduledir}/yaml_terminus/lib/puppet/indirector/facts':; '#{moduledir}/yaml_terminus/lib/puppet/indirector/node':; '#{moduledir}/yaml_terminus/lib/puppet/indirector/resource':; } file { '#{moduledir}/yaml_terminus/lib/puppet/indirector/catalog/#{storeconfigs_backend_name}.rb': ensure => file, content => ' require "puppet/indirector/catalog/yaml" class Puppet::Resource::Catalog::#{terminus_class_name} < Puppet::Resource::Catalog::Yaml def save(request) raise ArgumentError.new("You can only save objects that respond to :name") unless request.instance.respond_to?(:name) file = path(request.key) basedir = File.dirname(file) # This is quite likely a bad idea, since we are not managing ownership or modes. Dir.mkdir(basedir) unless Puppet::FileSystem.exist?(basedir) begin # We cannot dump anonymous modules in yaml, so dump to json File.open(file, "w") { |f| f.write request.instance.to_json } rescue TypeError => detail Puppet.err "Could not save \#{self.name} \#{request.key}: \#{detail}" end end def find(request) nil end end ', } file { '#{moduledir}/yaml_terminus/lib/puppet/indirector/facts/#{storeconfigs_backend_name}.rb': ensure => file, content => ' require "puppet/indirector/facts/yaml" class Puppet::Node::Facts::#{terminus_class_name} < Puppet::Node::Facts::Yaml def find(request) nil end end ', } file { '#{moduledir}/yaml_terminus/lib/puppet/indirector/node/#{storeconfigs_backend_name}.rb': ensure => file, content => ' require "puppet/indirector/node/yaml" class Puppet::Node::#{terminus_class_name} < Puppet::Node::Yaml def find(request) nil end end ', } file { '#{moduledir}/yaml_terminus/lib/puppet/indirector/resource/#{storeconfigs_backend_name}.rb': ensure => file, content => ' require "puppet/indirector/yaml" require "puppet/resource/catalog" class Puppet::Resource::#{terminus_class_name} < Puppet::Indirector::Yaml desc "Read resource instances from cached catalogs" def search(request) catalog_dir = File.join(Puppet.run_mode.server? ? Puppet[:yamldir] : Puppet[:clientyamldir], "catalog", "*") results = Dir.glob(catalog_dir).collect { |file| catalog = Puppet::Resource::Catalog.convert_from(:json, File.read(file)) if catalog.name == request.options[:host] next end catalog.resources.select { |resource| resource.type == request.key && resource.exported }.map! { |res| data_hash = res.to_data_hash parameters = data_hash["parameters"].map do |name, value| Puppet::Parser::Resource::Param.new(:name => name, :value => value) end attrs = {:parameters => parameters, :scope => request.options[:scope]} result = Puppet::Parser::Resource.new(res.type, res.title, attrs) result.collector_id = "\#{catalog.name}|\#{res.type}|\#{res.title}" result } }.flatten.compact results end end ', } # all the filtering is taken care of in the terminii # so any tests on filtering belong with puppetdb or pe file { '#{environmentpath}/#{tmp_environment}/manifests/site.pp': ensure => file, content => ' node "#{master.hostname}" { @@user{"#{exported_username}": ensure => present,} } node "default" { # collect resources on all nodes (puppet prevents collection on same node) User<<| |>> } ', } MANIFEST apply_manifest_on(master, manifest, :catch_failures => true) end # must specify environment in puppet.conf for it to pickup the terminus code in an environment module # but we have to bounce the server to pickup the storeconfigs... config anyway # we can't use with_puppet_running_on here because it uses puppet resource to bounce the server # puppet resource tries to use yaml_storeconfig's path() which doesn't exist # and fails back to yaml which indicates an attempted directory traversal and fails. # we could implemnt path() properly, but i'm just going to start the server the old fashioned way # and... config set is broken and doesn't add a main section step 'turn on storeconfigs, start puppetserver the old fashioned way' do on(master, "cp #{File.join(master.puppet['confdir'],'puppet.conf')} #{File.join('','tmp')}") on(master, "echo [main] >> #{File.join(master.puppet['confdir'],'puppet.conf')}") on(master, "echo environment=#{tmp_environment} >> #{File.join(master.puppet['confdir'],'puppet.conf')}") on(master, puppet('config set storeconfigs true --section main')) on(master, puppet("config set storeconfigs_backend #{storeconfigs_backend_name} --section main")) on(master, "service #{master['puppetservice']} restart") step 'run the master agent to export the resources' do on(master, puppet("agent -t --environment #{tmp_environment}")) end agents.each do |agent| next if agent == master step 'run the agents to collect exported resources' do on(agent, puppet("agent -t --environment #{tmp_environment}"), :acceptable_exit_codes => 2) on(agent, puppet_resource("user #{exported_username}"), :accept_all_exit_codes => true) do |result| assert_match(/present/, result.stdout, 'collected resource not found') end end end end end puppetlabs-puppet-789f600/acceptance/tests/language/functions_in_puppet_language.rb000066400000000000000000000134121470131746300307760ustar00rootroot00000000000000test_name 'Puppet executes functions written in the Puppet language' tag 'audit:high', 'audit:integration', 'audit:refactor', # use mk_tmp_environment_with_teardown helper for environment construction 'server' teardown do on master, 'rm -rf /etc/puppetlabs/code/modules/jenny' on master, 'rm -rf /etc/puppetlabs/code/environments/tommy' on master, 'rm -rf /etc/puppetlabs/code/environments/production/modules/one' on master, 'rm -rf /etc/puppetlabs/code/environments/production/modules/three' agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step 'Create some functions' do manifest = <<-EOF File { ensure => 'present', owner => 'root', group => 'root', mode => '0644', } file {['/etc/puppetlabs/', '/etc/puppetlabs/code/', '/etc/puppetlabs/code/modules/', '/etc/puppetlabs/code/modules/jenny', '/etc/puppetlabs/code/modules/jenny/functions', '/etc/puppetlabs/code/modules/jenny/functions/nested', '/etc/puppetlabs/code/environments', '/etc/puppetlabs/code/environments/production', '/etc/puppetlabs/code/environments/production/modules', '/etc/puppetlabs/code/environments/production/modules/one', '/etc/puppetlabs/code/environments/production/modules/one/functions', '/etc/puppetlabs/code/environments/production/modules/one/manifests', '/etc/puppetlabs/code/environments/production/modules/three', '/etc/puppetlabs/code/environments/production/modules/three/functions', '/etc/puppetlabs/code/environments/production/modules/three/manifests', '/etc/puppetlabs/code/environments/tommy', '/etc/puppetlabs/code/environments/tommy/modules', '/etc/puppetlabs/code/environments/tommy/modules/two', '/etc/puppetlabs/code/environments/tommy/modules/two/functions', ]: ensure => directory, mode => '0755', } # "Global" functions, no env file { '/etc/puppetlabs/code/modules/jenny/functions/mini.pp': content => 'function jenny::mini($a, $b) {if $a <= $b {$a} else {$b}}', require => File['/etc/puppetlabs/code/modules/jenny/functions'], } file { '/etc/puppetlabs/code/modules/jenny/functions/nested/maxi.pp': content => 'function jenny::nested::maxi($a, $b) {if $a >= $b {$a} else {$b}}', require => File['/etc/puppetlabs/code/modules/jenny/functions/nested'], } # Module "one", "production" env file { '/etc/puppetlabs/code/environments/production/modules/one/functions/foo.pp': content => 'function one::foo() {"This is the one::foo() function in the production environment"}', require => File['/etc/puppetlabs/code/environments/production/modules/one/functions'], } file { '/etc/puppetlabs/code/environments/production/modules/one/manifests/init.pp': content => 'class one { }', require => File['/etc/puppetlabs/code/environments/production/modules/one/manifests'], } # Module "three", "production" env file { '/etc/puppetlabs/code/environments/production/modules/three/functions/baz.pp': content => 'function three::baz() {"This is the three::baz() function in the production environment"}', require => File['/etc/puppetlabs/code/environments/production/modules/three/functions'], } file { '/etc/puppetlabs/code/environments/production/modules/three/manifests/init.pp': content => 'class three { }', require => File['/etc/puppetlabs/code/environments/production/modules/three/functions'], } # Module "two", "tommy" env file { '/etc/puppetlabs/code/environments/tommy/modules/two/functions/bar.pp': content => 'function two::bar() {"This is the two::bar() function in the tommy environment"}', require => File['/etc/puppetlabs/code/environments/tommy/modules/two/functions'], } EOF apply_manifest_on(master, manifest, {:catch_failures => true, :acceptable_exit_codes => [0,1]}) end manifest = <<-MANIFEST notice 'jenny::mini(1, 2) =', jenny::mini(1,2) notice 'jenny::nested::maxi(1, 2) =', jenny::nested::maxi(1,2) notice 'one::foo() =', one::foo() require 'one'; notice 'three::baz() =', three::baz() MANIFEST rc = apply_manifest_on(master, manifest, {:accept_all_exit_codes => true,}) step 'Call a global function' do fail_test 'Failed to call a "global" function' \ unless rc.stdout.include?('jenny::mini(1, 2) = 1') end step 'Call a global nested function' do fail_test 'Failed to call a "global" nested function' \ unless rc.stdout.include?('jenny::nested::maxi(1, 2) = 2') end step 'Call an env-specific function' do fail_test 'Failed to call a function defined in the current environment' \ unless rc.stdout.include?('This is the one::foo() function in the production environment') end step 'Call a function defined in an un-included module' do fail_test 'Failed to call a function defined in an un-required module' \ unless rc.stdout.include?('This is the three::baz() function in the production environment') end manifest = <<-MANIFEST.strip notice "two::bar() =", two::bar() MANIFEST # This should fail step 'Call a function not defined in the current environment' do rc = on master, puppet("apply -e '#{manifest}' --environment production"), {:accept_all_exit_codes => true,} fail_test 'Should not be able to call a function not defined in the current environment' \ unless rc.stderr.include?("Error: Evaluation Error: Unknown function: 'two::bar'") end step 'Call an env-specific function in a non-default environment' do rc = on master, puppet("apply -e '#{manifest}' --environment tommy") fail_test 'Failed to call env-specific function from that environment' \ unless rc.stdout.include?('This is the two::bar() function in the tommy environment') end puppetlabs-puppet-789f600/acceptance/tests/language/objects_in_catalog.rb000066400000000000000000000023311470131746300266470ustar00rootroot00000000000000test_name 'C99627: can use Object types in the catalog and apply/agent' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:integration', 'audit:refactor' # The use of apply on a reference system should # be adequate to test puppet. Running this in # context of server/agent should not be necessary. manifest = <<-PP type Mod::Foo = Object[{ attributes => { 'name' => String, 'size' => Integer[0, default] } }] define mod::foo_notifier(Mod::Foo $foo) { notify { $foo.name: } } class mod { mod::foo_notifier { xyz: foo => Mod::Foo('A foo', 42) } } include mod PP agents.each do |agent| # This is currently only expected to work with apply as the custom data type # definition will not be present on the agent to deserialize properly step "apply manifest on agent #{agent.hostname} and assert notify output" do apply_manifest_on(agent, manifest) do |result| assert(result.exit_code == 0, "agent didn't exit properly: (#{result.exit_code})") assert_match(/A foo/, result.stdout, 'agent didn\'t notify correctly') end end end end puppetlabs-puppet-789f600/acceptance/tests/language/pcore_generate_env_isolation.rb000066400000000000000000000061031470131746300307520ustar00rootroot00000000000000test_name 'C98345: ensure puppet generate assures env. isolation' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:integration', 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) tmp_environment2 = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" fq_tmp_environmentpath2 = "#{environmentpath}/#{tmp_environment2}" type_name = 'conflicting' relative_type_dir = 'modules/conflict/lib/puppet/type' relative_type_path = "#{relative_type_dir}/#{type_name}.rb" step 'create custom type in two environments' do on(master, "mkdir -p #{fq_tmp_environmentpath}/#{relative_type_dir}") on(master, "mkdir -p #{fq_tmp_environmentpath2}/#{relative_type_dir}") custom_type1 = <<-END Puppet::Type.newtype(:#{type_name}) do newparam :name, :namevar => true END custom_type2 = "#{custom_type1}" custom_type2 << " newparam :other\n" custom_type1 << " end\n" custom_type2 << " end\n" create_remote_file(master, "#{fq_tmp_environmentpath}/#{relative_type_path}", custom_type1) create_remote_file(master, "#{fq_tmp_environmentpath2}/#{relative_type_path}", custom_type2) site_pp1 = <<-PP notify{$environment:} #{type_name}{"somename":} PP site_pp2 = <<-PP notify{$environment:} #{type_name}{"somename": other => "uhoh"} PP create_sitepp(master, tmp_environment, site_pp1) create_sitepp(master, tmp_environment2, site_pp2) end on master, "chmod -R 755 /tmp/#{tmp_environment}" on master, "chmod -R 755 /tmp/#{tmp_environment2}" with_puppet_running_on(master,{}) do agents.each do |agent| on(agent, puppet("agent -t --environment #{tmp_environment}"), :acceptable_exit_codes => 2) step 'run agent in environment with type with an extra parameter. try to use this parameter' do on(agent, puppet("agent -t --environment #{tmp_environment2}"), :accept_all_exit_codes => true) do |result| unless agent['locale'] == 'ja' assert_match("Error: no parameter named 'other'", result.output, 'did not produce environment isolation issue as expected') end end end end step 'generate pcore files' do on(master, puppet("generate types --environment #{tmp_environment}")) on(master, puppet("generate types --environment #{tmp_environment2}")) end agents.each do |agent| step 'rerun agents after generate, ensure proper runs' do on(agent, puppet("agent -t --environment #{tmp_environment}"), :acceptable_exit_codes => 2) on(agent, puppet("agent -t --environment #{tmp_environment2}"), :acceptable_exit_codes => 2) end end end end pcore_resource_types_should_have_precedence_over_ruby.rb000066400000000000000000000070511470131746300360600ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/languagetest_name 'C98097 - generated pcore resource types should be loaded instead of ruby for custom types' do tag 'audit:high', 'audit:integration', 'audit:refactor', # use `mk_tmp_environment_with_teardown` helper to build environment 'server' environment = 'production' step 'setup - install module with custom ruby resource type' do #{{{ testdir = master.tmpdir('c98097') codedir = "#{testdir}/codedir" site_manifest_content =< true) do desc "Name of mycustomtype instance" $stderr.puts "this indicates that we are running ruby code and should not be seen when running generated pcore resource" end def refresh end end EOM apply_manifest_on(master, < true) File { ensure => directory, mode => "0755", } file {[ '#{codedir}', '#{codedir}/environments', '#{codedir}/environments/#{environment}', '#{codedir}/environments/#{environment}/manifests', '#{codedir}/environments/#{environment}/modules', '#{codedir}/environments/#{environment}/modules/mymodule', '#{codedir}/environments/#{environment}/modules/mymodule/manifests', '#{codedir}/environments/#{environment}/modules/mymodule/lib', '#{codedir}/environments/#{environment}/modules/mymodule/lib/puppet', '#{codedir}/environments/#{environment}/modules/mymodule/lib/puppet/type' ]: } file { '#{codedir}/environments/#{environment}/manifests/site.pp': ensure => file, content => '#{site_manifest_content}', } file { '#{codedir}/environments/#{environment}/modules/mymodule/lib/puppet/type/mycustomtype.rb': ensure => file, content => '#{custom_type_content}', } MANIFEST conf_opts = { 'main' => { 'environmentpath' => "#{codedir}/environments" } } backup_file = backup_the_file(master, puppet_config(master, 'confdir', section: 'master'), testdir, 'puppet.conf') lay_down_new_puppet_conf master, conf_opts, testdir teardown do restore_puppet_conf_from_backup( master, backup_file ) # See PUP-6995 on(master, "rm -f #{puppet_config(master, 'yamldir', section: 'master')}/node/*.yaml") agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end #}}} catalog_results = {} catalog_results[master.hostname] = { 'ruby_cat' => '', 'pcore_cat' => '' } step 'compile catalog using ruby resource' do on master, puppet('catalog', 'find', master.hostname) do |result| assert_match(/running ruby code/, result.stderr) catalog_results[master.hostname]['ruby_cat'] = JSON.parse(result.stdout.sub(/^[^{]+/,'')) end end step 'generate pcore type from ruby type' do on master, puppet('generate', 'types', '--environment', environment) end step 'compile catalog and make sure that ruby code is NOT executed' do on master, puppet('catalog', 'find', master.hostname) do |result| refute_match(/running ruby code/, result.stderr) catalog_results[master.hostname]['pcore_cat'] = JSON.parse(result.stdout.sub(/^[^{]+/,'')) end end step 'ensure that the resources created in the catalog using ruby and pcore are the same' do assert_equal(catalog_results[master.hostname]['ruby_cat']['resources'], catalog_results[master.hostname]['pcore_cat']['resources']) end end end puppetlabs-puppet-789f600/acceptance/tests/language/resource_refs_with_nested_arrays.rb000066400000000000000000000013201470131746300316570ustar00rootroot00000000000000test_name "#7681: Allow using array variables in resource references" tag 'audit:high', 'audit:unit' agents.each do |agent| test_manifest = < "#{agent.echo('the first command')}", path => "#{agent.path}", logoutput => true, } exec { "second": command => "#{agent.echo('the second command')}", path => "#{agent.path}", logoutput => true, } exec { "third": command => "#{agent.echo('the final command')}", path => "#{agent.path}", logoutput => true, require => Exec[$exec_names], } MANIFEST apply_manifest_on(agent, test_manifest) do |result| assert_match(/Exec\[third\].*the final command/, result.stdout) end end puppetlabs-puppet-789f600/acceptance/tests/language/sensitive_data_type.rb000066400000000000000000000164511470131746300271110ustar00rootroot00000000000000test_name 'C98120, C98077: Sensitive Data is redacted on CLI, logs, reports' do require 'puppet/acceptance/puppet_type_test_tools.rb' extend Puppet::Acceptance::PuppetTypeTestTools tag 'audit:high', 'audit:acceptance', # Tests that sensitive data is retains integrity # between server and agent transport/application. # Leaving at acceptance layer due to validate # written logs. 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) tmp_filename_win = tmp_filename_else = '' agents.each do |agent| # ugh... this won't work with more than two agents of two types if agent.platform =~ /32$/ tmp_filename_win = "C:\\cygwin\\tmp\\#{tmp_environment}.txt" else tmp_filename_win = "C:\\cygwin64\\tmp\\#{tmp_environment}.txt" end tmp_filename_else = "/tmp/#{tmp_environment}.txt" on agent, "echo 'old content' > /tmp/#{tmp_environment}.txt" end # first attempts at a reasonable table driven test. needs API work # FIXME: # expand this to other resource types, make parameters arbitrary, make assertions arbitrary # FIXME: add context messaging to each instance notify_redacted = 'Sensitive \[value redacted\]' file_redacted = 'changed \[redacted\] to \[redacted\]' test_resources = [ {:type => 'notify', :parameters => {:namevar => "1:${Sensitive.new('sekrit1')}"}, :assertions => [{:refute_match => 'sekrit1'}, {:assert_match => "1:#{notify_redacted}"}]}, {:type => 'notify', :parameters => {:namevar => "2:${Sensitive.new($meh2)}"}, :pre_code => '$meh2="sekrit2"', :assertions => [{:refute_match => 'sekrit2'}, {:assert_match => "2:#{notify_redacted}"}]}, {:type => 'notify', :parameters => {:namevar => "3:meh", :message => '"3:${Sensitive.new(\'sekrit3\')}"'}, :assertions => [{:refute_match => 'sekrit3'}, {:assert_match => "3:#{notify_redacted}"}]}, {:type => 'notify', :parameters => {:namevar => "4:meh", :message => "Sensitive.new($meh4)"}, :pre_code => '$meh4="sekrit4"', :assertions => [{:refute_match => 'sekrit4'}, {:assert_match => file_redacted}]}, {:type => 'notify', :parameters => {:namevar => "5:meh", :message => "$meh5"}, :pre_code => '$meh5=Sensitive.new("sekrit5")', :assertions => [{:refute_match => 'sekrit5'}, {:assert_match => file_redacted}]}, {:type => 'notify', :parameters => {:namevar => "6:meh", :message => '"6:${meh6}"'}, :pre_code => '$meh6=Sensitive.new("sekrit6")', :assertions => [{:refute_match => 'sekrit6'}, {:assert_match => "6:#{notify_redacted}"}]}, {:type => 'notify', :parameters => {:namevar => "7:${Sensitive('sekrit7')}"}, :assertions => [{:refute_match => 'sekrit7'}, {:assert_match => "7:#{notify_redacted}"}]}, # unwrap(), these should be en-clair {:type => 'notify', :parameters => {:namevar => "8:${unwrap(Sensitive.new('sekrit8'))}"}, :assertions => {:assert_match => "8:sekrit8"}}, {:type => 'notify', :parameters => {:namevar => "9:meh", :message => '"9:${unwrap(Sensitive.new(\'sekrit9\'))}"'}, :assertions => {:assert_match => "9:sekrit9"}}, {:type => 'notify', :parameters => {:namevar => "A:meh", :message => '"A:${unwrap($mehA)}"'}, :pre_code => '$mehA=Sensitive.new("sekritA")', :assertions => {:assert_match => "A:sekritA"}}, {:type => 'notify', :parameters => {:namevar => "B:meh", :message => '"B:${$mehB.unwrap}"'}, :pre_code => '$mehB=Sensitive.new("sekritB")', :assertions => {:assert_match => "B:sekritB"}}, {:type => 'notify', :parameters => {:namevar => "C:meh", :message => '"C:${$mehC.unwrap |$unwrapped| { "blk_${unwrapped}_blk" } } nonblk_${mehC}_nonblk"'}, :pre_code => '$mehC=Sensitive.new("sekritC")', :assertions => {:assert_match => ["C:blk_sekritC_blk", "nonblk_#{notify_redacted}_nonblk"]}}, # for --show_diff {:type => 'file', :parameters => {:namevar => "$pup_tmp_filename", :content => "Sensitive.new('sekritD')"}, :pre_code => "$pup_tmp_filename = if $facts['os']['family'] == 'windows' { '#{tmp_filename_win}' } else { '#{tmp_filename_else}' }", :assertions => [{:refute_match => 'sekritD'}, {:assert_match => /#{tmp_environment}\.txt..content. #{file_redacted}/}]}, ] sitepp_content = generate_manifest(test_resources) assertion_code = generate_assertions(test_resources) # Make a copy of the full set of 'test_resources' but filtered down to include # only the assertions of type ':refute_match'. So for example, where the # 'test_resources' array might have an entry like this... # # {:type => 'notify', ... # :assertions => [{:refute_match => 'sekrit1'}, # {:assert_match => "1:#{notify_redacted}"}]} # # ... the ':assert_match' entry would be filtered out in the new # 'refutation_resources' array, producing: # # {:type => 'notify', ... # :assertions => [{:refute_match => 'sekrit1'}]} # # This is done so that when validating the log output, we can refute the # existence of any of the sensitive info in the log without having to # assert that redacted info is in the log. The redacted info appears in # the console output from the Puppet agent run - by virtue of including a # '--debug' flag on the agent command line - whereas the redacted info is not # expected to be piped into the log. refutation_resources = test_resources.collect do |assertion_group| refutation_group = assertion_group.clone refutation_group[:assertions] = assertion_group[:assertions].select do |assertion| assertion.has_key?(:refute_match) end refutation_group end refutation_code = generate_assertions(refutation_resources) create_sitepp(master, tmp_environment, sitepp_content) step "run agent in #{tmp_environment}, run all assertions" do with_puppet_running_on(master,{}) do agents.each do |agent| # redirect logging to a temp location to avoid platform specific syslogs on(agent, puppet("agent -t --debug --trace --show_diff --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert_equal(result.exit_code, 2,'puppet agent run failed') run_assertions(assertion_code, result) unless agent['locale'] == 'ja' step "assert no redacted data in log" do run_assertions(refutation_code, result) end end # don't do this before the agent log scanning, above. it will skew the results step "assert no redacted data in vardir" do # no recursive grep in solaris :facepalm: on(agent, "find #{agent.puppet['vardir']} -type f | xargs grep sekrit", :accept_all_exit_codes => true) do |result| refute_match(/sekrit(1|2|3|6|7)/, result.stdout, 'found redacted data we should not have') #TODO: if/when this is fixed, we should just be able to eval(assertion_code_ in this result block also! expect_failure 'file resource contents will end up in the cached catalog en-clair' do refute_match(/sekritD/, result.stdout, 'found redacted file data we should not have') end end end end end end end puppetlabs-puppet-789f600/acceptance/tests/language/server_set_facts.rb000066400000000000000000000032761470131746300264100ustar00rootroot00000000000000test_name 'C64667: ensure server_facts is set and error if any value is overwritten by an agent' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:acceptance', # Validating server/client interaction 'server' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) step 'ensure $server_facts exist' do create_sitepp(master, tmp_environment, <<-SITE) notify{"abc$server_facts":} SITE master_opts = {} with_puppet_running_on(master, master_opts) do agents.each do |agent| on(agent, puppet("agent -t --environment #{tmp_environment}"), :acceptable_exit_codes => 2) do |result| assert_match(/abc{serverversion/, result.stdout, "#{agent}: $server_facts should have some stuff" ) end end end end step 'ensure puppet issues a warning if an agent overwrites a server fact' do agents.each do |agent| on(agent, puppet("agent -t", 'ENV' => { 'FACTER_server_facts' => 'overwrite' }), :acceptable_exit_codes => 1) do |result| # Do not perform this check on non-English hosts unless agent['locale'] == 'ja' assert_match(/Error.*Attempt to assign to a reserved variable name: 'server_facts'/, result.stderr, "#{agent}: $server_facts should error if overwritten" ) end end end end end puppetlabs-puppet-789f600/acceptance/tests/loader/000077500000000000000000000000001470131746300221755ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/loader/autoload_from_resource_type_decl.rb000066400000000000000000000145261470131746300313240ustar00rootroot00000000000000test_name 'C100303: Resource type statement triggered auto-loading works both with and without generated types' do tag 'risk:high' require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/agent_fqdn_utils' extend Puppet::Acceptance::AgentFqdnUtils # create the file and make sure its empty and accessible by everyone def empty_execution_log_file(host, path) create_remote_file(host, path, '') on(host, "chmod 777 '#{path}'") end app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" relative_type_dir = 'modules/one/lib/puppet/type' relative_type_path = "#{relative_type_dir}/type_tst.rb" execution_log = {} execution_log[agent_to_fqdn(master)] = master.tmpfile('master_autoload_resource') agents.each do |agent| execution_log[agent_to_fqdn(agent)] = agent.tmpfile('agent_autoload_resource') end teardown do on(master, "rm -f '#{execution_log[agent_to_fqdn(master)]}'") agents.each do |agent| on(agent, "rm -f '#{execution_log[agent_to_fqdn(agent)]}'") on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step 'create custom type' do on(master, "mkdir -p '#{fq_tmp_environmentpath}/#{relative_type_dir}'") # create a custom type that will write out to a different file on each agent # this way we can verify whether the newtype code was executed on each system custom_type = <<-END Puppet::Type.newtype(:type_tst) do newparam(:name, :namevar => true) do fqdn = Facter.value('networking.fqdn') if fqdn == '#{agent_to_fqdn(master)}' File.open("#{execution_log[agent_to_fqdn(master)]}", 'a+') { |f| f.puts("found_type_tst: " + Time.now.to_s) } end END agents.each do |agent| custom_type << <<-END if fqdn == '#{agent_to_fqdn(agent)}' File.open("#{execution_log[agent_to_fqdn(agent)]}", 'a+') { |f| f.puts("found_type_tst: " + Time.now.to_s) } end END end custom_type << <<-END Puppet.notice("found_type_tst") end end END create_remote_file(master, "#{fq_tmp_environmentpath}/#{relative_type_path}", custom_type) site_pp = <<-PP Resource['type_tst'] { 'found_type': } PP create_sitepp(master, tmp_environment, site_pp) end on(master, "chmod -R 755 '/tmp/#{tmp_environment}'") # when the agent does its run, the newtype is executed on both the agent and master nodes # so we should see a message in the execution log file on the agent and the master agents.each do |agent| with_puppet_running_on(master, {}) do empty_execution_log_file(master, execution_log[agent_to_fqdn(master)]) empty_execution_log_file(agent, execution_log[agent_to_fqdn(agent)]) on(agent, puppet("agent -t --environment '#{tmp_environment}'")) do |puppet_result| assert_match(/\/File\[.*\/type_tst.rb\]\/ensure: defined content as/, puppet_result.stdout, 'Expected to see defined content message for type: type_tst') assert_match(/Notice: found_type_tst/, puppet_result.stdout, 'Expected to see the notice from the new type: type_tst') end on(master, "cat '#{execution_log[agent_to_fqdn(master)]}'") do |cat_result| assert_match(/found_type_tst:/, cat_result.stdout, "Expected to see execution log entry on master #{agent_to_fqdn(master)}") end on(agent, "cat '#{execution_log[agent_to_fqdn(agent)]}'") do |cat_result| assert_match(/found_type_tst:/, cat_result.stdout, "Expected to see execution log entry on agent #{agent_to_fqdn(agent)}") end end end # when generating the pcore the newtype should only be run on the master node step 'generate pcore files' do # start with an empty execution log empty_execution_log_file(master, execution_log[agent_to_fqdn(master)]) agents.each do |agent| empty_execution_log_file(agent, execution_log[agent_to_fqdn(agent)]) end on(master, puppet("generate types --environment '#{tmp_environment}'")) do |puppet_result| assert_match(/Notice: Generating '\/.*\/type_tst\.pp' using 'pcore' format/, puppet_result.stdout, 'Expected to see Generating message for type: type_tst') assert_match(/Notice: found_type_tst/, puppet_result.stdout, 'Expected to see log entry on master ') end # we should see a log entry on the master node on(master, "cat '#{execution_log[agent_to_fqdn(master)]}'") do |cat_result| assert_match(/found_type_tst:/, cat_result.stdout, "Expected to see execution log entry on master #{agent_to_fqdn(master)}") end # we should not see any log entries on any of the agent nodes agents.each do |agent| next if agent == master on(agent, "cat '#{execution_log[agent_to_fqdn(agent)]}'") do |cat_result| assert_empty(cat_result.stdout.chomp, "Expected execution log file to be empty on agent node #{agent_to_fqdn(agent)}") end end end empty_execution_log_file(master, execution_log[agent_to_fqdn(master)]) agents.each do |agent| next if agent == master empty_execution_log_file(agent, execution_log[agent_to_fqdn(agent)]) # this test is relying on the beaker helper with_puppet_running_on() to restart the server # Compilation should now work using the generated types, # so we should only see a log entry on the agent node and nothing on the master node with_puppet_running_on(master, {}) do on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 0) do |puppet_result| assert_match(/Notice: found_type_tst/, puppet_result.stdout, 'Expected to see output from new type: type_tst') end end on(agent, "cat '#{execution_log[agent_to_fqdn(agent)]}'") do |cat_result| assert_match(/found_type_tst:/, cat_result.stdout, "Expected to see an execution log entry on agent #{agent_to_fqdn(agent)}") end end on(master, "cat '#{execution_log[agent_to_fqdn(master)]}'") do |cat_result| assert_empty(cat_result.stdout.chomp, "Expected master execution log to be empty #{agent_to_fqdn(master)}") end end puppetlabs-puppet-789f600/acceptance/tests/loader/func4x_loadable_from_modules.rb000066400000000000000000000063451470131746300303370ustar00rootroot00000000000000test_name "Exercise a module with 4x function and 4x system function" # Purpose: # Test that a packed puppet can call a 4x system function, and that a 4x function in # a module can be called. # # Method: # * Manually construct a very simple module with a manifest that creates a file. # * The file has content that depends on logic that calls both a system function (reduce), and # a function supplied in the module (helloworld::mul10). # * The module is manually constructed to allow the test to also run on Windows where the module tool # is not supported. # * The module is included by calling 'include' from 'puppet apply'. # * Puppet apply is executed to generate the file with the content. # * The generated contents is asserted. # TODO: The test can be improved by adding yet another module that calls the function in helloworld. # TODO: The test can be improved to also test loading of a non namespaced function require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils tag 'audit:high', 'audit:unit' # This should be covered adequately by unit tests initialize_temp_dirs agents.each do |agent| # The modulepath to use in environment 'dev' envs_path = get_test_file_path(agent, 'environments') dev_modulepath = get_test_file_path(agent, 'environments/dev/modules') target_path = get_test_file_path(agent, 'output') mkdirs agent, target_path # make sure that we use the modulepath from the dev environment puppetconf = get_test_file_path(agent, 'puppet.conf') on agent, puppet("config", "set", "environmentpath", envs_path, "--section", "main", "--config", puppetconf) on agent, puppet("config", "set", "environment", "dev", "--section", "user", "--config", puppetconf) # Where the functions in the written modules should go helloworld_functions = 'helloworld/lib/puppet/functions/helloworld' # Clean out the module that will be written to ensure no interference from a previous run on agent, "rm -rf #{File.join(dev_modulepath, 'helloworld')}" mkdirs agent, File.join(dev_modulepath, helloworld_functions) # Write a module # Write the function helloworld::mul10, that multiplies its argument by 10 create_remote_file(agent, File.join(dev_modulepath, helloworld_functions, "mul10.rb"), <<'SOURCE') Puppet::Functions.create_function(:'helloworld::mul10') do def mul10(x) x * 10 end end SOURCE # Write a manifest that calls a 4x function (reduce), and calls a function defined in the module # (helloworld::mul10). # mkdirs agent, File.join(dev_modulepath, "helloworld", "manifests") create_remote_file(agent, File.join(dev_modulepath, "helloworld", "manifests", "init.pp"), < 'file', mode => '0666', content => [1,2,3].reduce("Generated") |$memo, $n| { "${memo}, ${n} => ${helloworld::mul10($n)}" } } } SOURCE # Run apply to generate the file with the output on(agent, puppet('apply', '-e', "'include helloworld'", '--config', puppetconf)) # Assert that the file was written with the generated content on(agent, "cat #{File.join(target_path, 'result.txt')}") do |result| assert_match(/^Generated, 1 => 10, 2 => 20, 3 => 30$/, result.stdout, "Generated the wrong content") end end puppetlabs-puppet-789f600/acceptance/tests/loader/resource_triggers_autoload.rb000066400000000000000000000040301470131746300301440ustar00rootroot00000000000000test_name 'C100296: can auto-load defined types using a Resource statement' do tag 'risk:high' require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end relative_define_type_dir = 'modules/one/manifests' relative_define_type_1_path = "#{relative_define_type_dir}/tst1.pp" relative_define_type_2_path = "#{relative_define_type_dir}/tst2.pp" step 'create custom type in two environments' do on(master, "mkdir -p #{fq_tmp_environmentpath}/#{relative_define_type_dir}") define_type_1 = <<-END define one::tst1($var) { notify { "tst1: ${var}": } } END define_type_2 = <<-END define one::tst2($var) { notify { "tst2: ${var}": } } END create_remote_file(master, "#{fq_tmp_environmentpath}/#{relative_define_type_1_path}", define_type_1) create_remote_file(master, "#{fq_tmp_environmentpath}/#{relative_define_type_2_path}", define_type_2) site_pp = <<-PP each(['tst1', 'tst2']) |$nr| { Resource["one::${nr}"] { "some_title_${nr}": var => "Define found one::${nr}" } } PP create_sitepp(master, tmp_environment, site_pp) end on(master, "chmod -R 755 /tmp/#{tmp_environment}") with_puppet_running_on(master, {}) do agents.each do |agent| on(agent, puppet("agent -t --environment #{tmp_environment}"), :acceptable_exit_codes => 2) do |puppet_result| assert_match(/Notice: tst1: Define found one::tst1/, puppet_result.stdout, 'Expected to see output from define notify') assert_match(/Notice: tst2: Define found one::tst2/, puppet_result.stdout, 'Expected to see output from define notify') end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/000077500000000000000000000000001470131746300222405ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/lookup/config3_interpolation.rb000066400000000000000000000052071470131746300270700ustar00rootroot00000000000000test_name 'C99578: lookup should allow interpolation in hiera3 configs' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:integration', 'audit:refactor', # This test specifically tests interpolation on the master. # Recommend adding an additonal test that validates # lookup in a masterless setup. 'server' app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" master_confdir = puppet_config(master, 'confdir', section: 'master') hiera_conf_backup = master.tmpfile('C99578-hiera-yaml') step "backup global hiera.yaml" do on(master, "cp -a #{master_confdir}/hiera.yaml #{hiera_conf_backup}", :acceptable_exit_codes => [0,1]) end teardown do on(master, "mv #{hiera_conf_backup} #{master_confdir}/hiera.yaml", :acceptable_exit_codes => [0,1]) agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "create hiera configs in #{tmp_environment} and global" do step "create global hiera.yaml and module data" do create_remote_file(master, "#{master_confdir}/hiera.yaml", <<-HIERA) --- :backends: - "yaml" :hierarchy: - "%{calling_class_path}" - "%{calling_class}" - "%{calling_module}" - "common" HIERA on(master, "mkdir -p #{fq_tmp_environmentpath}/hieradata/") on(master, "mkdir -p #{fq_tmp_environmentpath}/modules/some_mod/manifests") create_remote_file(master, "#{fq_tmp_environmentpath}/modules/some_mod/manifests/init.pp", <<-PP) class some_mod { notify { "${lookup('environment_key')}": } } PP create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/some_mod.yaml", <<-YAML) --- environment_key: "env value" YAML create_sitepp(master, tmp_environment, <<-SITE) include some_mod SITE on(master, "chmod -R 775 #{fq_tmp_environmentpath}") on(master, "chmod -R 775 #{master_confdir}") end end with_puppet_running_on(master,{}) do agents.each do |agent| step "agent lookup" do on(agent, puppet('agent', "-t --environment #{tmp_environment} --debug"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup didn't exit properly: (#{result.exit_code})") assert_match(/env value/, result.stdout, "agent lookup didn't find correct key") end end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/config5_interpolation.rb000066400000000000000000000111241470131746300270650ustar00rootroot00000000000000test_name 'C99578: hiera5 lookup config with interpolated scoped nested variables' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:integration', 'audit:refactor', # This test specifically tests interpolation on the master. # Recommend adding an additonal test that validates # lookup in a masterless setup. 'server' app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type + '1') fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "create environment hiera5.yaml and environment data" do create_remote_file(master, "#{fq_tmp_environmentpath}/hiera.yaml", <<-HIERA) --- version: 5 defaults: datadir: 'hieradata' data_hash: yaml_data hierarchy: - name: "Global settings" path: "global.yaml" - name: "Role specific settings" paths: - "roles/%{::roles.0}.yaml" - name: "Other Role specific settings" paths: - "roles/%{roles2.0}.yaml" - name: "scoped variable" paths: - "roles/%{::myclass::myvar.0}.yaml" - name: "nested hash variable" paths: - "roles/%{::hash_array.key1.0}.yaml" HIERA on(master, "mkdir -p #{fq_tmp_environmentpath}/hieradata/roles") create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/global.yaml", <<-YAML) roles: - test1 roles2: - test2 data: - "from global" YAML create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/roles/test1.yaml", <<-YAML) data: - 'from test1' YAML create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/roles/test2.yaml", <<-YAML) data: - 'from test2' YAML create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/roles/test3.yaml", <<-YAML) data: - 'from test3' YAML create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/roles/test4.yaml", <<-YAML) data: - 'from test4' YAML create_sitepp(master, tmp_environment, <<-SITE) class myclass { $myvar = ['test3'] } include myclass $hash_array = {key1 => ['test4']} $roles = lookup('roles') $data = lookup('data', Array[String], 'unique') notify{"data: ${data}":} $hiera_array_data = hiera_array('data') notify{"hiera_array_data: ${hiera_array_data}":} $roles2 = lookup('roles2') $data2 = lookup('data', Array[String], 'unique') notify{"data2: ${data2}":} $hiera_array_data2 = hiera_array('data') notify{"hiera_array_data2: ${hiera_array_data2}":} SITE on(master, "chmod -R 775 #{fq_tmp_environmentpath}") end with_puppet_running_on(master,{}) do agents.each do |agent| step "agent lookups: #{agent.hostname}, hiera5" do on(agent, puppet('agent', "-t --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup didn't exit properly: (#{result.exit_code})") assert_match(/data: \[from global, from test1/, result.stdout, "agent lookup didn't interpolate with hiera value") assert_match(/hiera_array_data: \[from global, from test1/, result.stdout, "agent hiera_array didn't interpolate with hiera value") assert_match(/data2: \[from global, from test1, from test2/, result.stdout, "agent lookup didn't interpolate non-global scope with hiera value") assert_match(/hiera_array_data2: \[from global, from test1, from test2/, result.stdout, "agent hiera_array didn't interpolate non-global scope with hiera value") assert_match(/data2: \[from global, from test1, from test2, from test3/, result.stdout, "agent lookup didn't interpolate class scope with hiera value") assert_match(/hiera_array_data2: \[from global, from test1, from test2, from test3/, result.stdout, "agent hiera_array didn't interpolate class scope with hiera value") assert_match(/data2: \[from global, from test1, from test2, from test3, from test4\]/, result.stdout, "agent lookup didn't interpolate nested hashes with hiera value") assert_match(/hiera_array_data2: \[from global, from test1, from test2, from test3, from test4\]/, result.stdout, "agent hiera_array didn't interpolate nested hashes with hiera value") end end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/hiera3_custom_backend.rb000066400000000000000000000101131470131746300267750ustar00rootroot00000000000000test_name 'C99630: hiera v3 custom backend' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/temp_file_utils.rb' extend Puppet::Acceptance::TempFileUtils tag 'audit:high', 'audit:acceptance', 'audit:refactor', # Master is not needed for this test. Refactor # to use puppet apply with a local module tree. app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" puppetserver_config = "#{master['puppetserver-confdir']}/puppetserver.conf" existing_loadpath = read_tk_config_string(on(master, "cat #{puppetserver_config}").stdout.strip)['jruby-puppet']['ruby-load-path'].first confdir = puppet_config(master, 'confdir', section: 'master') hiera_conf_backup = master.tmpfile('C99629-hiera-yaml') step "backup global hiera.yaml" do on(master, "cp -a #{confdir}/hiera.yaml #{hiera_conf_backup}", :acceptable_exit_codes => [0,1]) end teardown do step 'delete custom backend, restore default hiera config' do on(master, "rm #{existing_loadpath}/hiera/backend/custom_backend.rb", :acceptable_exit_codes => [0,1]) on(master, "mv #{hiera_conf_backup} #{confdir}/hiera.yaml", :acceptable_exit_codes => [0,1]) on(master, "/opt/puppetlabs/server/bin/puppetserver gem uninstall --executables --force hiera") on(master, "/opt/puppetlabs/puppet/bin/gem uninstall --executables --force hiera") end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "install hiera v3 gem" do # for puppet agent <-> server, hiera must be installed using puppetserver's gem command on(master, "/opt/puppetlabs/server/bin/puppetserver gem install --no-document hiera") # for puppet lookup, hiera must be installed using puppet's gem command on(master, "/opt/puppetlabs/puppet/bin/gem install --no-document hiera") end step "create hiera v5 config and v3 custom backend" do on(master, "cp #{confdir}/hiera.yaml /tmp") create_remote_file(master, "#{confdir}/hiera.yaml", <<-HIERA) --- version: 5 hierarchy: - name: Test hiera3_backend: custom HIERA on(master, "chmod -R #{PUPPET_CODEDIR_PERMISSIONS} #{confdir}") on(master, "mkdir -p #{existing_loadpath}/hiera/backend/") custom_backend_rb = <<-RB class Hiera module Backend class Custom_backend def lookup(key, scope, order_override, resolution_type, context) return 'custom value' unless (key == 'lookup_options') end end end end RB create_remote_file(master, "#{existing_loadpath}/hiera/backend/custom_backend.rb", custom_backend_rb) on(master, "chmod #{PUPPET_CODEDIR_PERMISSIONS} #{existing_loadpath}/hiera/backend/custom_backend.rb") end step "create site.pp which calls lookup on our keys" do create_sitepp(master, tmp_environment, <<-SITE) notify { "${lookup('anykey')}": } SITE on(master, "chmod -R #{PUPPET_CODEDIR_PERMISSIONS} #{fq_tmp_environmentpath}") end step 'assert lookups using lookup subcommand on the master' do on(master, puppet('lookup', "--environment #{tmp_environment}", '--explain', 'anykey'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/custom value/, result.stdout, "lookup subcommand didn't find correct key") end end with_puppet_running_on(master,{}) do agents.each do |agent| step "agent manifest lookup on #{agent.hostname}" do on(agent, puppet('agent', "-t --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup didn't exit properly: (#{result.exit_code})") assert_match(/custom value/, result.stdout, "agent lookup didn't find correct key") end end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/lookup.rb000066400000000000000000000322011470131746300240740ustar00rootroot00000000000000test_name "Lookup data using the agnostic lookup function" do # pre-docs: # https://puppet-on-the-edge.blogspot.com/2015/01/puppet-40-data-in-modules-and.html tag 'audit:high', 'audit:acceptance', 'audit:refactor', # Master is not needed for this test. Refactor # to use puppet apply with a local module tree. # Use mk_tmp_environment_with_teardown to create environment. 'server' testdir = master.tmpdir('lookup') module_name = "data_module" module_name2 = "other_module" hash_name = "hash_name" array_key = "array_key" env_data_implied_key = "env_data_implied" env_data_implied_value = "env_implied_a" env_data_key = "env_data" env_data_value = "env_a" env_hash_key = "env_hash_key" env_hash_value = "env_class_a" env_array_value0 = "env_array_a" env_array_value1 = "env_array_b" module_data_implied_key = "module_data_implied" module_data_implied_value = "module_implied_b" module_data_key = "module_data" module_data_value = "module_b" module_data_value_other = "other_module_b" module_hash_key = "module_hash_key" module_hash_value = "module_class_b" module_array_value0 = "module_array_a" module_array_value1 = "module_array_b" env_data_override_implied_key = "env_data_override_implied" env_data_override_implied_value = "env_override_implied_c" env_data_override_key = "env_data_override" env_data_override_value = "env_override_c" hiera_data_implied_key = "apache_server_port_implied" hiera_data_implied_value = "8080" hiera_data_key = "apache_server_port" hiera_data_value = "9090" hiera_hash_key = "hiera_hash_key" hiera_hash_value = "hiera_class_c" hiera_array_value0 = "hiera_array_a" hiera_array_value1 = "hiera_array_b" automatic_data_key = "automatic_data_key" automatic_data_value = "automatic_data_value" automatic_default_value = "automatic_default_value" def mod_manifest_entry(module_name = nil, testdir, module_data_implied_key, module_data_implied_value, module_data_key, module_data_value, hash_name, module_hash_key, module_hash_value, array_key, module_array_value0, module_array_value1) if module_name module_files_manifest = < file, content => " Puppet::Functions.create_function(:'#{module_name}::data') do def data() { '#{module_name}::#{module_data_implied_key}' => '#{module_data_implied_value}', '#{module_name}::#{module_data_key}' => '#{module_data_value}', '#{module_name}::#{hash_name}' => {'#{module_hash_key}' => '#{module_hash_value}'}, '#{module_name}::#{array_key}' => ['#{module_array_value0}', '#{module_array_value1}'] } end end ", mode => "0640", } PP module_files_manifest end end def mod_manifest_metadata_json(module_name = nil, testdir) if module_name < file, content => ' { "name": "tester-#{module_name}", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [ ], "data_provider": "function" } ', mode => "0644", } file { '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/bindings': ensure => absent, force => true, } PPmetadata end end teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end module_manifest1 = mod_manifest_entry(module_name, testdir, module_data_implied_key, module_data_implied_value, module_data_key, module_data_value, hash_name, module_hash_key, module_hash_value, array_key, module_array_value0, module_array_value1) module_manifest2 = mod_manifest_entry(module_name2, testdir, module_data_implied_key, module_data_implied_value, module_data_key, module_data_value_other, hash_name, module_hash_key, module_hash_value, array_key, module_array_value0, module_array_value1) metadata_manifest1 = mod_manifest_metadata_json(module_name, testdir) metadata_manifest2 = mod_manifest_metadata_json(module_name2, testdir) apply_manifest_on(master, <<-PP, :catch_failures => true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}':; '#{testdir}/hieradata':; '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/production/modules':; '#{testdir}/environments/production/lib':; '#{testdir}/environments/production/lib/puppet':; '#{testdir}/environments/production/lib/puppet/functions':; '#{testdir}/environments/production/lib/puppet/functions/environment':; '#{testdir}/environments/production/modules/#{module_name}':; '#{testdir}/environments/production/modules/#{module_name}/manifests':; '#{testdir}/environments/production/modules/#{module_name}/lib':; '#{testdir}/environments/production/modules/#{module_name}/lib/puppet':; '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/bindings':; '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/bindings/#{module_name}':; '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/functions':; '#{testdir}/environments/production/modules/#{module_name}/lib/puppet/functions/#{module_name}':; '#{testdir}/environments/production/modules/#{module_name2}':; '#{testdir}/environments/production/modules/#{module_name2}/manifests':; '#{testdir}/environments/production/modules/#{module_name2}/lib':; '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet':; '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/bindings':; '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/bindings/#{module_name2}':; '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/functions':; '#{testdir}/environments/production/modules/#{module_name2}/lib/puppet/functions/#{module_name2}':; } file { '#{testdir}/hiera.yaml': ensure => file, content => '--- :backends: - "yaml" :logger: "console" :hierarchy: - "global" :yaml: :datadir: "#{testdir}/hieradata" ', mode => "0640", } file { '#{testdir}/hieradata/global.yaml': ensure => file, content => "--- #{hiera_data_key}: #{hiera_data_value} #{module_name}::#{hiera_data_implied_key}: #{hiera_data_implied_value} #{module_name}::#{hash_name}: #{hiera_hash_key}: #{hiera_hash_value} #{module_name}::#{array_key}: - #{hiera_array_value0} - #{hiera_array_value1} #{module_name}::#{automatic_data_key}: #{automatic_data_value} ", mode => "0640", } file { '#{testdir}/environments/production/environment.conf': ensure => file, content => ' environment_timeout = 0 # for this environment, provide our own function to supply data to lookup # implies a ruby function in /lib/puppet/functions/environment/data.rb # named environment::data() environment_data_provider = "function" ', mode => "0640", } # the function to provide data for this environment file { '#{testdir}/environments/production/lib/puppet/functions/environment/data.rb': ensure => file, content => " Puppet::Functions.create_function(:'environment::data') do def data() { '#{module_name}::#{env_data_implied_key}' => '#{env_data_implied_value}', '#{module_name}::#{env_data_override_implied_key}' => '#{env_data_override_implied_value}', '#{env_data_key}' => '#{env_data_value}', '#{module_name}::#{hash_name}' => {'#{env_hash_key}' => '#{env_hash_value}'}, '#{env_data_override_key}' => '#{env_data_override_value}', '#{module_name}::#{array_key}' => ['#{env_array_value0}', '#{env_array_value1}'] } end end ", mode => "0640", } # place module file segments here #{module_manifest1} # same key, different module and values #{module_manifest2} file { '#{testdir}/environments/production/modules/#{module_name}/manifests/init.pp': ensure => file, content => ' class #{module_name}($#{env_data_implied_key}, $#{module_data_implied_key}, $#{env_data_override_implied_key}, $#{hiera_data_implied_key}, $#{automatic_data_key}=$#{automatic_default_value}) { # lookup data from the environment function databinding notify { "#{env_data_implied_key} $#{env_data_implied_key}": } $lookup_env = lookup("#{env_data_key}") notify { "#{env_data_key} $lookup_env": } # lookup data from the module databinding notify { "#{module_data_implied_key} $#{module_data_implied_key}": } $lookup_module = lookup("#{module_name}::#{module_data_key}") notify { "#{module_data_key} $lookup_module": } # lookup data from another modules databinding $lookup_module2 = lookup("#{module_name2}::#{module_data_key}") notify { "#{module_data_key} $lookup_module2": } # ensure env can override module notify { "#{env_data_override_implied_key} $#{env_data_override_implied_key}": } $lookup_override = lookup("#{env_data_override_key}") notify { "#{env_data_override_key} $lookup_override": } # should fall-back to hiera global.yaml data notify { "#{hiera_data_implied_key} $#{hiera_data_implied_key}": } $lookup_port = lookup("#{hiera_data_key}") notify { "#{hiera_data_key} $lookup_port": } # should be able to merge hashes across sources # this mimicks/covers behavior for including classes $lookup_hash = lookup("#{module_name}::#{hash_name}",Hash[String,String],\\'hash\\') notify { "#{hash_name} $lookup_hash": } # should be able to make an array across sources # this mimicks/covers behavior for including classes $lookup_array = lookup("#{module_name}::#{array_key}",Array[String],\\'unique\\') notify { "yep": message => "#{array_key} $lookup_array" } # automatic data lookup of parametrized class notify { "#{automatic_data_key} $#{automatic_data_key}": } }', mode => "0640", } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, content => " node default { include #{module_name} }", mode => "0640", } PP apply_manifest_on(master, <<-PP, :catch_failures => true) #{metadata_manifest1} #{metadata_manifest2} PP master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", 'hiera_config' => "#{testdir}/hiera.yaml", }, } with_puppet_running_on master, master_opts, testdir do step "Lookup string data, binding specified in metadata.json" do agents.each do |agent| on(agent, puppet('agent', "-t"), :acceptable_exit_codes => [0, 2]) do |result| assert_match("#{env_data_implied_key} #{env_data_implied_value}", result.stdout) assert_match("#{env_data_key} #{env_data_value}", result.stdout) assert_match("#{module_data_implied_key} #{module_data_implied_value}", result.stdout) assert_match("#{module_data_key} #{module_data_value}", result.stdout) assert_match("#{module_data_key} #{module_data_value_other}", result.stdout) assert_match("#{env_data_override_implied_key} #{env_data_override_implied_value}", result.stdout) assert_match("#{env_data_override_key} #{env_data_override_value}", result.stdout) assert_match("#{hiera_data_implied_key} #{hiera_data_implied_value}", result.stdout) assert_match("#{hiera_data_key} #{hiera_data_value}", result.stdout) assert_match("#{hash_name} {#{module_hash_key} => #{module_hash_value}, #{env_hash_key} => #{env_hash_value}, #{hiera_hash_key} => #{hiera_hash_value}}", result.stdout) assert_match("#{array_key} [#{hiera_array_value0}, #{hiera_array_value1}, #{env_array_value0}, #{env_array_value1}, #{module_array_value0}, #{module_array_value1}]", result.stdout) assert_match("#{automatic_data_key} #{automatic_data_value}", result.stdout) end end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/lookup_rich_values.rb000066400000000000000000000122071470131746300264640ustar00rootroot00000000000000test_name 'C99044: lookup should allow rich data as values' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:acceptance', 'audit:refactor', # Master is not needed for this test. Refactor # to use puppet apply with a local environment. 'server' # The following two lines are required for the puppetserver service to # start correctly. These should be removed when PUP-7102 is resolved. confdir = puppet_config(master, 'confdir', section: 'master') on(master, "chown puppet:puppet #{confdir}/hiera.yaml") app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" sensitive_value_rb = 'foot, no mouth' sensitive_value_pp = 'toe, no step' sensitive_value_pp2 = 'toe, no module' teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "create ruby lookup function in #{tmp_environment}" do on(master, "mkdir -p #{fq_tmp_environmentpath}/lib/puppet/functions/environment") create_remote_file(master, "#{fq_tmp_environmentpath}/hiera.yaml", <<-HIERA) --- version: 5 hierarchy: - name: Test data_hash: rich_data_test - name: Test2 data_hash: some_mod::rich_data_test2 - name: Test3 data_hash: rich_data_test3 HIERA create_remote_file(master, "#{fq_tmp_environmentpath}/lib/puppet/functions/rich_data_test.rb", <<-FUNC) Puppet::Functions.create_function(:rich_data_test) do def rich_data_test(options, context) rich_type_instance = Puppet::Pops::Types::PSensitiveType::Sensitive.new("#{sensitive_value_rb}") { 'environment_key' => rich_type_instance, } end end FUNC end step "create puppet language lookup function in #{tmp_environment} module" do on(master, "mkdir -p #{fq_tmp_environmentpath}/modules/some_mod/functions") create_remote_file(master, "#{fq_tmp_environmentpath}/modules/some_mod/functions/rich_data_test2.pp", <<-FUNC) function some_mod::rich_data_test2($options, $context) { { "environment_key2" => Sensitive('#{sensitive_value_pp}'), } } FUNC on(master, "chmod -R a+rw #{fq_tmp_environmentpath}") end step "C99571: create puppet language lookup function in #{tmp_environment}" do on(master, "mkdir -p #{fq_tmp_environmentpath}/functions") create_remote_file(master, "#{fq_tmp_environmentpath}/functions/rich_data_test3.pp", <<-FUNC) function rich_data_test3($options, $context) { { "environment_key3" => Sensitive('#{sensitive_value_pp2}'), } } FUNC on(master, "chmod -R a+rw #{fq_tmp_environmentpath}") end step "create site.pp which calls lookup on our keys" do create_sitepp(master, tmp_environment, <<-SITE) notify { "${unwrap(lookup('environment_key'))}": } notify { "${unwrap(lookup('environment_key2'))}": } notify { "${unwrap(lookup('environment_key3'))}": } SITE end step 'assert lookups using lookup subcommand' do on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "lookup subcommand using ruby function didn't exit properly: (#{result.exit_code})") assert_match(sensitive_value_rb, result.stdout, "lookup subcommand using ruby function didn't find correct key") end on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key2'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "lookup subcommand using puppet function in module didn't exit properly: (#{result.exit_code})") assert_match(sensitive_value_pp, result.stdout, "lookup subcommand using puppet function in module didn't find correct key") end on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key3'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "lookup subcommand using puppet function didn't exit properly: (#{result.exit_code})") assert_match(sensitive_value_pp2, result.stdout, "lookup subcommand using puppet function didn't find correct key") end end with_puppet_running_on(master,{}) do agents.each do |agent| step "agent lookup in ruby function" do on(agent, puppet('agent', "-t --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup using ruby function didn't exit properly: (#{result.exit_code})") assert_match(sensitive_value_rb, result.stdout, "agent lookup using ruby function didn't find correct key") assert_match(sensitive_value_pp, result.stdout, "agent lookup using puppet function in module didn't find correct key") assert_match(sensitive_value_pp2, result.stdout, "agent lookup using puppet function didn't find correct key") end end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/merge_strategies.rb000066400000000000000000000210741470131746300261220ustar00rootroot00000000000000test_name 'C99903: merge strategies' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:acceptance', 'audit:refactor', # Master is not needed for this test. Refactor # to use puppet apply with a local module tree. app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type + '1') fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" tmp_environment2 = mk_tmp_environment_with_teardown(master, app_type + '2') fq_tmp_environmentpath2 = "#{environmentpath}/#{tmp_environment2}" master_confdir = puppet_config(master, 'confdir', section: 'master') hiera_conf_backup = master.tmpfile(app_type) teardown do step "restore default global hiera.yaml" do on(master, "mv #{hiera_conf_backup} #{master_confdir}/hiera.yaml", :acceptable_exit_codes => [0,1]) end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "create global hiera.yaml and environment data" do step "backup global hiera.yaml" do on(master, "cp -a #{master_confdir}/hiera.yaml #{hiera_conf_backup}") end create_remote_file(master, "#{master_confdir}/hiera.yaml", <<-HIERA) --- :backends: - yaml :yaml: :datadir: "/etc/puppetlabs/code/environments/%{::environment}/hieradata" :hierarchy: - "host" - "roles" - "profiles" - "%{facts.os.name}" - "%{facts.os.family}" - "%{facts.kernel}" - "common" :merge_behavior: deeper :deep_merge_options: :merge_hash_arrays: true HIERA on(master, "chown puppet:puppet #{master_confdir}/hiera.yaml") on(master, "mkdir -p #{fq_tmp_environmentpath}/hieradata/") create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/host.yaml", <<-YAML) --- profiles: webserver: apache: httpd: modules: - mpm_prefork - php - ssl arrayed_hash: the_hash: - array1: key1: val1 key2: val2 array: - foo YAML create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/profiles.yaml", <<-YAML) profiles: webserver: apache: httpd: modules: - auth_kerb - authnz_ldap - cgid - php - status array: - bar YAML create_sitepp(master, tmp_environment, <<-SITE) notify { "hiera_hash: ${hiera_hash ('profiles')['webserver']['apache']['httpd']['modules']}": } notify { "lookup1: ${lookup ('profiles')['webserver']['apache']['httpd']['modules']}": } notify { "lookup1b: ${lookup ({'name' => 'profiles', 'merge' => 'deep'})['webserver']['apache']['httpd']['modules']}": } notify { "hiera_merge_hash: ${hiera_hash ('arrayed_hash')}": } notify { "lookup_arrayed_hash: ${lookup ({'name' => 'arrayed_hash', 'merge' => {'strategy' => 'deep', 'merge_hash_arrays' => true}})}": } notify { "hiera-array: ${hiera ('array')}": } notify { "hiera_array: ${hiera_array ('array')}": } notify { "lookup-array: ${lookup ('array')}": } SITE on(master, "chmod -R 775 #{fq_tmp_environmentpath}") end step "create another environment, hiera5 config and environment data: #{tmp_environment2}" do create_remote_file(master, "#{fq_tmp_environmentpath2}/hiera.yaml", <<-HIERA) --- version: 5 hierarchy: - name: "%{environment}/host" data_hash: yaml_data path: "hieradata/host.yaml" - name: "%{environment}/profiles" data_hash: yaml_data path: "hieradata/profiles.yaml" HIERA on(master, "mkdir -p #{fq_tmp_environmentpath2}/hieradata/") create_remote_file(master, "#{fq_tmp_environmentpath2}/hieradata/host.yaml", <<-YAML) --- profiles: webserver: apache: httpd: modules: - mpm_prefork - php - ssl arrayed_hash: the_hash: - array1: key1: val1 key2: val2 array: - foo lookup_options: 'profiles': merge: strategy: deep YAML create_remote_file(master, "#{fq_tmp_environmentpath2}/hieradata/profiles.yaml", <<-YAML) profiles: webserver: apache: httpd: modules: - auth_kerb - authnz_ldap - cgid - php - status array: - bar lookup_options: 'profiles': merge: strategy: deep YAML create_sitepp(master, tmp_environment2, <<-SITE) notify { "hiera_hash: ${hiera_hash ('profiles')['webserver']['apache']['httpd']['modules']}": } notify { "lookup2: ${lookup ('profiles')['webserver']['apache']['httpd']['modules']}": } notify { "lookup2b: ${lookup ({'name' => 'profiles', 'merge' => 'first'})['webserver']['apache']['httpd']['modules']}": } notify { "hiera_merge_hash: ${hiera_hash ('arrayed_hash')}": } notify { "lookup_arrayed_hash: ${lookup ({'name' => 'arrayed_hash', 'merge' => {'strategy' => 'deep', 'merge_hash_arrays' => true}})}": } notify { "hiera-array: ${hiera ('array')}": } notify { "hiera_array: ${hiera_array ('array')}": } notify { "lookup-array: ${lookup ('array')}": } SITE on(master, "chmod -R 775 #{fq_tmp_environmentpath2}") end with_puppet_running_on(master,{}) do agents.each do |agent| step "agent lookups #{agent.hostname}, hiera3" do on(agent, puppet('agent', "-t --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup didn't exit properly: (#{result.exit_code})") # hiera_hash will honor old global merge strategies, which were a bad idea assert_match(/hiera_hash: \[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\]/, result.stdout, "1: agent hiera_hash didn't find correct key") # so, lookup doesn't honor them except on a by-key or by-lookup basis assert_match(/lookup1: \[mpm_prefork, php, ssl\]/, result.stdout, "1: agent lookup didn't find correct key") assert_match(/lookup1b: \[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\]/, result.stdout, "1b: agent lookup didn't find correct key") assert_match(/hiera_merge_hash: {the_hash => \[{array1 => {key1 => val1, key2 => val2}}\]}/, result.stdout, "agent hiera_hash 1 merge_hash_arrays didn't work properly") assert_match(/lookup_arrayed_hash: {the_hash => \[{array1 => {key1 => val1, key2 => val2}}\]}/, result.stdout, "agent lookup 1 deep merge with merge_hash_arrays didn't work properly") assert_match(/hiera-array: \[foo\]/, result.stdout, "hiera() lookup of an array with deeper should be merged") assert_match(/hiera_array: \[foo, bar\]/, result.stdout, "hiera_array() lookup of an array should be merged") assert_match(/lookup-array: \[foo\]/, result.stdout, "lookup() lookup of an array should default to first") end end step "agent lookups #{agent.hostname}, hiera5" do on(agent, puppet('agent', "-t --environment #{tmp_environment2}"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup didn't exit properly: (#{result.exit_code})") assert_match(/hiera_hash: \[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\]/, result.stdout, "2: agent hiera_hash didn't find correct key") assert_match(/lookup2: \[auth_kerb, authnz_ldap, cgid, php, status, mpm_prefork, ssl\]/, result.stdout, "2: agent lookup didn't find correct key") assert_match(/lookup2b: \[mpm_prefork, php, ssl\]/, result.stdout, "2b: agent lookup didn't find correct key") assert_match(/hiera_merge_hash: {the_hash => \[{array1 => {key1 => val1, key2 => val2}}\]}/, result.stdout, "agent hiera_hash 2 merge_hash_arrays didn't work properly") assert_match(/lookup_arrayed_hash: {the_hash => \[{array1 => {key1 => val1, key2 => val2}}\]}/, result.stdout, "agent lookup 2 deep merge with merge_hash_arrays didn't work properly") assert_match(/hiera-array: \[foo\]/, result.stdout, "hiera() 2 lookup in hiera5 of an array should default to first") assert_match(/hiera_array: \[foo, bar\]/, result.stdout, "hiera_array() 2 lookup of an array should be merged") assert_match(/lookup-array: \[foo\]/, result.stdout, "lookup() 2 lookup in hiera5 of an array should default to first") end end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/v3_config_and_data.rb000066400000000000000000000122261470131746300262600ustar00rootroot00000000000000test_name 'C99629: hiera v5 can use v3 config and data' do require 'puppet/acceptance/environment_utils.rb' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:acceptance', 'audit:refactor', # Master is not needed for this test. Refactor # to use puppet apply with a local module tree. app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" hiera_conf_backup = master.tmpfile('C99629-hiera-yaml') step "create hiera v3 global config and data" do confdir = puppet_config(master, 'confdir', section: 'master') step "backup global hiera.yaml" do on(master, "cp -a #{confdir}/hiera.yaml #{hiera_conf_backup}", :acceptable_exit_codes => [0,1]) end teardown do step "restore global hiera.yaml" do on(master, "mv #{hiera_conf_backup} #{confdir}/hiera.yaml", :acceptable_exit_codes => [0,1]) end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "create global hiera.yaml and module data" do create_remote_file(master, "#{confdir}/hiera.yaml", <<-HIERA) --- :backends: - "yaml" - "json" - "hocon" :hierarchy: - "somesuch" - "common" HIERA on(master, "mkdir -p #{fq_tmp_environmentpath}/hieradata/") create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/somesuch.yaml", <<-YAML) --- environment_key1: "env value1" environment_key3: "env value3" YAML create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/somesuch.json", <<-JSON) { "environment_key1" : "wrong value", "environment_key2" : "env value2" } JSON step "C99628: add hocon backend and data" do create_remote_file(master, "#{fq_tmp_environmentpath}/hieradata/somesuch.conf", <<-HOCON) environment_key4 = "hocon value", HOCON end create_sitepp(master, tmp_environment, <<-SITE) notify { "${lookup('environment_key1')}": } notify { "${lookup('environment_key2')}": } notify { "${lookup('environment_key3')}": } notify { "${lookup('environment_key4')}": } SITE on(master, "chmod -R 775 #{fq_tmp_environmentpath}") on(master, "chmod -R 775 #{confdir}") end end step 'assert lookups using lookup subcommand' do step 'assert lookup --explain using lookup subcommand' do on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key1 --explain'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "1: lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/env value1/, result.stdout, "1: lookup subcommand didn't find correct key") assert_match(/hiera configuration version 3/, result.stdout, "hiera config version not reported properly") assert_match(/#{fq_tmp_environmentpath}\/hieradata\/somesuch\.yaml/, result.stdout, "hiera hierarchy abs path not reported properly") assert_match(/path: "somesuch"/, result.stdout, "hiera hierarchy path not reported properly") end end on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key2'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "2: lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/env value2/, result.stdout, "2: lookup subcommand didn't find correct key") end on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key3'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "3: lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/env value3/, result.stdout, "3: lookup subcommand didn't find correct key") end on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key4'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "4: lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/hocon value/, result.stdout, "4: lookup subcommand didn't find correct key") end end with_puppet_running_on(master,{}) do agents.each do |agent| step "agent lookup" do on(agent, puppet('agent', "-t --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup didn't exit properly: (#{result.exit_code})") assert_match(/env value1/, result.stdout, "1: agent lookup didn't find correct key") assert_match(/env value2/, result.stdout, "2: agent lookup didn't find correct key") assert_match(/env value3/, result.stdout, "3: agent lookup didn't find correct key") assert_match(/hocon value/, result.stdout, "4: agent lookup didn't find correct key") end end end end end puppetlabs-puppet-789f600/acceptance/tests/lookup/v4_hieradata_with_v5_configs.rb000066400000000000000000000116351470131746300303030ustar00rootroot00000000000000test_name 'C99572: v4 hieradata with v5 configs' do require 'puppet/acceptance/puppet_type_test_tools.rb' extend Puppet::Acceptance::PuppetTypeTestTools tag 'audit:high', 'audit:acceptance', 'audit:refactor', # Master is not needed for this test. Refactor # to use puppet apply with a local module tree. app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) fq_tmp_environmentpath = "#{environmentpath}/#{tmp_environment}" confdir = puppet_config(master, 'confdir', section: 'master') hiera_conf_backup = master.tmpfile('C99572-hiera-yaml') step "backup global hiera.yaml" do on(master, "cp -a #{confdir}/hiera.yaml #{hiera_conf_backup}", :acceptable_exit_codes => [0,1]) end teardown do step "restore global hiera.yaml" do on(master, "mv #{hiera_conf_backup} #{confdir}/hiera.yaml", :acceptable_exit_codes => [0,1]) end agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end step "create global hiera.yaml and data" do create_remote_file(master, "#{confdir}/hiera.yaml", <<-HIERA) --- version: 5 hierarchy: - name: "%{environment}" data_hash: yaml_data path: "%{environment}.yaml" - name: common data_hash: yaml_data path: "common.yaml" HIERA on(master, "chmod 755 #{confdir}/hiera.yaml") create_remote_file(master, "#{confdir}/#{tmp_environment}.yaml", <<-YAML) --- environment_key: environment_key-global_env_file global_key: global_key-global_env_file YAML create_remote_file(master, "#{confdir}/common.yaml", <<-YAML) --- environment_key: environment_key-global_common_file global_key: global_key-global_common_file YAML end step "create environment hiera.yaml and data" do on(master, "mkdir -p #{fq_tmp_environmentpath}/data") create_remote_file(master, "#{fq_tmp_environmentpath}/hiera.yaml", <<-HIERA) --- version: 5 hierarchy: - name: "%{environment}" data_hash: yaml_data path: "%{environment}.yaml" - name: common data_hash: yaml_data path: "common.yaml" - name: hocon data_hash: hocon_data path: "common.conf" HIERA create_remote_file(master, "#{fq_tmp_environmentpath}/data/#{tmp_environment}.yaml", <<-YAML) --- environment_key: "environment_key-env_file" YAML create_remote_file(master, "#{fq_tmp_environmentpath}/data/common.yaml", <<-YAML) --- environment_key: "environment_key-common_file" global_key: "global_key-common_file" YAML step "C99628: add hocon backend and data" do create_remote_file(master, "#{fq_tmp_environmentpath}/data/common.conf", <<-HOCON) environment_key2 = "hocon value", HOCON end create_sitepp(master, tmp_environment, <<-SITE) notify { "${lookup('environment_key')}": } notify { "${lookup('global_key')}": } notify { "${lookup('environment_key2')}": } SITE on(master, "chmod -R 755 #{fq_tmp_environmentpath}") end step 'assert lookups using lookup subcommand' do on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "1: lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/environment_key-env_file/, result.stdout, 'lookup environment_key subcommand didn\'t find correct key') end on(master, puppet('lookup', "--environment #{tmp_environment}", 'global_key'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "2: lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/global_key-common_file/, result.stdout, 'lookup global_key subcommand didn\'t find correct key') end on(master, puppet('lookup', "--environment #{tmp_environment}", 'environment_key2'), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 0, "3: lookup subcommand didn't exit properly: (#{result.exit_code})") assert_match(/hocon value/, result.stdout, 'lookup environment_key2 subcommand didn\'t find correct key') end end with_puppet_running_on(master,{}) do agents.each do |agent| step 'agent lookup' do on(agent, puppet('agent', "-t --environment #{tmp_environment}"), :accept_all_exit_codes => true) do |result| assert(result.exit_code == 2, "agent lookup didn't exit properly: (#{result.exit_code})") assert_match(/global_key-common_file/m, result.stdout, 'agent lookup didn\'t find global key') assert_match(/environment_key-env_file/m, result.stdout, 'agent lookup didn\'t find environment_key') assert_match(/hocon value/m, result.stdout, 'agent lookup didn\'t find environment_key2') end end end end end puppetlabs-puppet-789f600/acceptance/tests/modulepath.rb000066400000000000000000000105451470131746300234230ustar00rootroot00000000000000test_name 'Supports vendored modules' do tag 'risk:high' # beacon custom type emits a message so we can tell where the # type was loaded from, e.g. vendored, global, and whether the # type was loaded locally or pluginsynced from the master. def beacon_type(message) return < true) newproperty(:message) do def sync; true; end def retrieve; :absent; end def insync?(is); false; end defaultto { "#{message}" } end end END end def global_modules(host) if host.platform =~ /windows/ '/cygdrive/c/ProgramData/PuppetLabs/code/modules' else '/etc/puppetlabs/code/modules' end end def vendor_modules(host) if host.platform =~ /windows/ # escape spaces "/cygdrive/c/Program\\ Files/Puppet\\ Labs/Puppet/puppet/vendor_modules" else '/opt/puppetlabs/puppet/vendor_modules' end end teardown do hosts.each do |host| on(host, "rm -rf #{vendor_modules(host)}/beacon") on(host, "rm -rf #{global_modules(host)}/beacon") libdir = host.puppet['vardir'] on(host, "rm -rf #{libdir}") end on(master, "rm -rf /etc/puppetlabs/code/environments/production/modules/beacon") on(master, "rm -f /etc/puppetlabs/code/environments/production/manifests/site.pp") end step 'delete libdir' do hosts.each do |host| on(host, "rm -rf #{host.puppet['libdir']}") end end step 'create vendored module with a custom type' do hosts.each do |host| vendor_dir = vendor_modules(host) on(host, "mkdir -p #{vendor_dir}/beacon/lib/puppet/type") # unescape, because net-scp escapes vendor_dir.gsub!(/\\/, '') create_remote_file(host, "#{vendor_dir}/beacon/lib/puppet/type/beacon.rb", beacon_type("vendored module from #{host}")) end end step 'vendored modules work locally' do hosts.each do |host| on(host, puppet("apply -e \"beacon { 'ping': }\"")) do |result| assert_match(/defined 'message' as 'vendored module from #{host}'/, result.stdout) end end end step 'vendored modules can be excluded' do hosts.each do |host| on(host, puppet("describe --vendormoduledir '' beacon"), accept_all_exit_codes: true) do |result| assert_match(/Unknown type beacon/, result.stdout) end end end step 'global modules override vendored modules' do agents.each do |agent| # skip the agent on the master, as we don't want to install the # global module on the master until later next if agent == master global_dir = global_modules(agent) on(agent, "mkdir -p #{global_dir}/beacon/lib/puppet/type") # global_dir doesn't have spaces, so don't need to escape create_remote_file(agent, "#{global_dir}/beacon/lib/puppet/type/beacon.rb", beacon_type("global module from #{agent}")) on(agent, puppet("apply -e \"beacon { 'ping': }\"")) do |result| assert_match(/defined 'message' as 'global module from #{agent}'/, result.stdout) end end end step "prepare server" do create_remote_file(master, "/etc/puppetlabs/code/environments/production/manifests/site.pp", "beacon { 'ping': }") on(master, "chown -R puppet:puppet /etc/puppetlabs/code/environments/production/manifests/site.pp") on(master, "chown -R puppet:puppet #{vendor_modules(master)}") end with_puppet_running_on(master, {}) do step "agent doesn't pluginsync the vendored module, instead using its local vendored module" do agents.each do |agent| on(agent, puppet("agent -t"), :acceptable_exit_codes => [0,2]) do |result| assert_match(/defined 'message' as 'vendored module from #{agent}'/, result.stdout) end end end step "agent downloads and uses newly installed global module from the server" do global_dir = global_modules(master) on(master, "mkdir -p #{global_dir}/beacon/lib/puppet/type") create_remote_file(master, "#{global_dir}/beacon/lib/puppet/type/beacon.rb", beacon_type("server module from #{master}")) on(master, "chown -R puppet:puppet #{global_dir}") agents.each do |agent| on(agent, puppet("agent -t"), :acceptable_exit_codes => [0,2]) do |result| assert_match(/defined 'message' as 'server module from #{master}'/, result.stdout) end end end end end puppetlabs-puppet-789f600/acceptance/tests/ordering/000077500000000000000000000000001470131746300225405ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/ordering/master_agent_application.rb000066400000000000000000000025321470131746300301230ustar00rootroot00000000000000test_name "Puppet applies resources without dependencies in file order over the network" tag 'audit:high', 'audit:integration', 'server' testdir = master.tmpdir('application_order') apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}':; '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/production/manifests/site.pp': ensure => file, mode => "0640", content => ' notify { "first": } notify { "second": } notify { "third": } notify { "fourth": } notify { "fifth": } notify { "sixth": } notify { "seventh": } notify { "eighth": } '; } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", } } with_puppet_running_on(master, master_opts) do agents.each do |agent| on(agent, puppet('agent', "--no-daemonize --onetime --verbose")) do |result| if result.stdout !~ /Notice: first.*Notice: second.*Notice: third.*Notice: fourth.*Notice: fifth.*Notice: sixth.*Notice: seventh.*Notice: eighth/m fail_test "Output did not include the notify resources in the correct order" end end end end puppetlabs-puppet-789f600/acceptance/tests/parser_functions/000077500000000000000000000000001470131746300243135ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/parser_functions/calling_all_functions.rb000066400000000000000000000364101470131746300311750ustar00rootroot00000000000000test_name 'Calling all functions.. test in progress!' tag 'audit:high', 'audit:acceptance' # create single manifest calling all functions step 'Apply manifest containing all function calls' def manifest_call_each_function_from_array(functions) manifest = '' # use index to work around puppet's immutable variables # use variables so we can concatenate strings functions.each_with_index do |function,index| if function[:rvalue] manifest << "$pre#{index} = \"sayeth #{function[:name].capitalize}: Scope(Class[main]): \" " manifest << "$output#{index} = #{function[:name]}(#{function[:args]}) " manifest << "#{function[:lambda]} notice \"${pre#{index}}${output#{index}}\"\n" else manifest << "$pre#{index} = \"sayeth #{function[:name].capitalize}: \" " manifest << "notice \"${pre#{index}}\"\n" manifest << "#{function[:name]}(#{function[:args]}) " manifest << "#{function[:lambda]}\n" end end manifest end agents.each do |agent| testdir = agent.tmpdir('calling_all_functions') # if agent["platform"] =~ /win/ # generator = {:args => '"c:/windows/system32/tasklist.exe"', :expected => /\nImage Name/} # else # generator = {:args => '"/bin/date"', :expected => /\w\w\w.*?\d\d:\d\d\:\d\d/} # end # create list of 3x functions and args # notes: hiera functions are well tested elsewhere, included for completeness # special cases: contain (call this from call_em_all) # do fail last because it errors out functions_3x = [ {:name => :alert, :args => '"consider yourself on alert"', :lambda => nil, :expected => 'consider yourself on alert', :rvalue => false}, {:name => :binary_file, :args => '"call_em_all/rickon.txt"', :lambda => nil, :expected => '', :rvalue => true}, #{:name => :break, :args => '', :lambda => nil, :expected => '', :rvalue => false}, # this is explicitly called from call_em_all module which is included below #{:name => :contain, :args => 'call_em_all', :lambda => nil, :expected => '', :rvalue => true}, # below doens't instance the resource. no output {:name => :create_resources, :args => 'notify, {"w"=>{message=>"winter is coming"}}', :lambda => nil, :expected => '', :rvalue => false}, {:name => :crit, :args => '"consider yourself critical"', :lambda => nil, :expected => 'consider yourself critical', :rvalue => false}, {:name => :debug, :args => '"consider yourself bugged"', :lambda => nil, :expected => '', :rvalue => false}, # no output expected unless run with debug {:name => :defined, :args => 'File["/tmp"]', :lambda => nil, :expected => 'false', :rvalue => true}, {:name => :dig, :args => '[100]', :lambda => nil, :expected => '[100]', :rvalue => true}, # Expect sha256 hash value for the digest {:name => :digest, :args => '"Sansa"', :lambda => nil, :expected => '4ebf3a5527313f06c7965749d7764c15cba6fe86da11691ca9bd0ce448563979', :rvalue => true}, {:name => :emerg, :args => '"consider yourself emergent"', :lambda => nil, :expected => 'consider yourself emergent', :rvalue => false}, {:name => :err, :args => '"consider yourself in err"', :lambda => nil, :expected => 'consider yourself in err', :rvalue => false}, {:name => :file, :args => '"call_em_all/rickon.txt"', :lambda => nil, :expected => 'who?', :rvalue => true}, {:name => :fqdn_rand, :args => '100000', :lambda => nil, :expected => /Fqdn_rand: Scope\(Class\[main\]\): \d{1,5}/, :rvalue => true}, # generate requires a fully qualified exe; which requires specifics for windows vs posix #{:name => :generate, :args => generator[:args], :lambda => nil, :expected => generator[:expected], :rvalue => true}, {:name => :hiera_array, :args => 'date,default_array', :lambda => nil, :expected => 'default_array', :rvalue => true}, {:name => :hiera_hash, :args => 'date,default_hash', :lambda => nil, :expected => 'default_hash', :rvalue => true}, {:name => :hiera_include, :args => 'date,call_em_all', :lambda => nil, :expected => '', :rvalue => false}, {:name => :hiera, :args => 'date,default_date', :lambda => nil, :expected => 'default_date', :rvalue => true}, {:name => :include, :args => 'call_em_all', :lambda => nil, :expected => '', :rvalue => false}, {:name => :info, :args => '"consider yourself informed"', :lambda => nil, :expected => '', :rvalue => false}, # no ouput unless in debug mode {:name => :inline_template, :args => '\'empty<%= @x %>space\'', :lambda => nil, :expected => 'emptyspace', :rvalue => true}, # test the living life out of this thing in lookup.rb, and it doesn't allow for a default value #{:name => :lookup, :args => 'date,lookup_date', :lambda => nil, :expected => '', :rvalue => true}, # well tested elsewhere {:name => :sha256, :args => '"Bran"', :lambda => nil, :expected => '824264f7f73d6026550b52a671c50ad0c4452af66c24f3784e30f515353f2ce0', :rvalue => true}, # Integer.new {:name => :Integer, :args => '"100"', :lambda => nil, :expected => '100', :rvalue => true}, {:name => :notice, :args => '"consider yourself under notice"', :lambda => nil, :expected => 'consider yourself under notice', :rvalue => false}, {:name => :realize, :args => 'User[arya]', :lambda => nil, :expected => '', :rvalue => false}, # TODO: create a virtual first {:name => :regsubst, :args => '"Cersei","Cer(\\\\w)ei","Daenery\\\\1"',:lambda => nil, :expected => 'Daenerys', :rvalue => true}, # explicitly called in call_em_all; implicitly called by the include above #{:name => :require, :args => '[4,5,6]', :lambda => nil, :expected => '', :rvalue => true}, # 4x output contains brackets around scanf output {:name => :scanf, :args => '"Eddard Stark","%6s"', :lambda => nil, :expected => '[Eddard]', :rvalue => true}, {:name => :sha1, :args => '"Sansa"', :lambda => nil, :expected => '4337ce5e4095e565d51e0ef4c80df1fecf238b29', :rvalue => true}, {:name => :shellquote, :args => '["-1", "--two"]', :lambda => nil, :expected => '-1 --two', :rvalue => true}, # 4x output contains brackets around split output and commas btwn values {:name => :split, :args => '"9,8,7",","', :lambda => nil, :expected => '[9, 8, 7]', :rvalue => true}, {:name => :sprintf, :args => '"%b","123"', :lambda => nil, :expected => '1111011', :rvalue => true}, {:name => :step, :args => '[100,99],1', :lambda => nil, :expected => 'Iterator[Integer]-Value', :rvalue => true}, # explicitly called in call_em_all #{:name => :tag, :args => '[4,5,6]', :lambda => nil, :expected => '', :rvalue => true}, {:name => :tagged, :args => '"yer_it"', :lambda => nil, :expected => 'false', :rvalue => true}, {:name => :template, :args => '"call_em_all/template.erb"', :lambda => nil, :expected => 'no defaultsno space', :rvalue => true}, {:name => :type, :args => '42', :lambda => nil, :expected => 'Integer[42, 42]', :rvalue => true}, {:name => :versioncmp, :args => '"1","2"', :lambda => nil, :expected => '-1', :rvalue => true}, {:name => :warning, :args => '"consider yourself warned"', :lambda => nil, :expected => 'consider yourself warned', :rvalue => false}, # do this one last or it will not allow the others to run. {:name => :fail, :args => '"Jon Snow"', :lambda => nil, :expected => /Error:.*Jon Snow/, :rvalue => false}, ] puppet_version = on(agent, puppet('--version')).stdout.chomp functions_4x = [ {:name => :assert_type, :args => '"String[1]", "Valar morghulis"', :lambda => nil, :expected => 'Valar morghulis', :rvalue => true}, {:name => :each, :args => '[1,2,3]', :lambda => '|$x| {$x}', :expected => '[1, 2, 3]', :rvalue => true}, {:name => :epp, :args => '"call_em_all/template.epp",{x=>droid}', :lambda => nil, :expected => 'This is the droid you are looking for!', :rvalue => true}, {:name => :filter, :args => '[4,5,6]', :lambda => '|$x| {true}', :expected => '[4, 5, 6]', :rvalue => true}, # find_file() called by binary_file #{:name => :find_file, :args => '[4,5,6]', :lambda => '|$x| {true}', :expected => '[4, 5, 6]', :rvalue => true}, {:name => :inline_epp, :args => '\'<%= $x %>\',{x=>10}', :lambda => nil, :expected => '10', :rvalue => true}, #{:name => :lest, :args => '100', :lambda => '"100"', :expected => '100', :rvalue => true}, {:name => :map, :args => '[7,8,9]', :lambda => '|$x| {$x * $x}', :expected => '[49, 64, 81]', :rvalue => true}, {:name => :match, :args => '"abc", /b/', :lambda => nil, :expected => '[b]', :rvalue => true}, #{:name => :next, :args => '100', :lambda => nil, :expected => '100', :rvalue => true}, {:name => :reduce, :args => '[4,5,6]', :lambda => '|$sum, $n| { $sum+$n }', :expected => '15', :rvalue => true}, #{:name => :return, :args => '100', :lambda => nil, :expected => '100', :rvalue => true}, {:name => :reverse_each, :args => '[100,99]', :lambda => nil, :expected => 'Iterator[Integer]-Value', :rvalue => true}, # :reuse,:recycle {:name => :slice, :args => '[1,2,3,4,5,6], 2', :lambda => nil, :expected => '[[1, 2], [3, 4], [5, 6]]', :rvalue => true}, {:name => :strftime, :args => 'Timestamp("4216-09-23T13:14:15.123 UTC"), "%C"', :lambda => nil, :expected => '42', :rvalue => true}, {:name => :then, :args => '100', :lambda => '|$x| {$x}', :expected => '100', :rvalue => true}, {:name => :with, :args => '1, "Catelyn"', :lambda => '|$x, $y| {"$x, $y"}', :expected => '1, Catelyn', :rvalue => true}, ] module_manifest = < directory, } file { '#{testdir}':; '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/modules':; '#{testdir}/environments/production/modules/tagged':; '#{testdir}/environments/production/modules/tagged/manifests':; '#{testdir}/environments/production/modules/contained':; '#{testdir}/environments/production/modules/contained/manifests':; '#{testdir}/environments/production/modules/required':; '#{testdir}/environments/production/modules/required/manifests':; '#{testdir}/environments/production/modules/call_em_all':; '#{testdir}/environments/production/modules/call_em_all/manifests':; '#{testdir}/environments/production/modules/call_em_all/templates':; '#{testdir}/environments/production/modules/call_em_all/files':; } file { '#{testdir}/environments/production/modules/tagged/manifests/init.pp': ensure => file, content => 'class tagged { notice tagged tag yer_it }', } file { '#{testdir}/environments/production/modules/required/manifests/init.pp': ensure => file, content => 'class required { notice required }', } file { '#{testdir}/environments/production/modules/contained/manifests/init.pp': ensure => file, content => 'class contained { notice contained }', } file { '#{testdir}/environments/production/modules/call_em_all/manifests/init.pp': ensure => file, content => 'class call_em_all { notice call_em_all contain contained require required tag yer_it }', } file { '#{testdir}/environments/production/modules/call_em_all/files/rickon.txt': ensure => file, content => 'who?', } file { '#{testdir}/environments/production/modules/call_em_all/templates/template.epp': ensure => file, content => 'This is the <%= $x %> you are looking for!', } file { '#{testdir}/environments/production/modules/call_em_all/templates/template.erb': ensure => file, content => 'no defaults<%= @x %>no space', } PP apply_manifest_on(agent, module_manifest, :catch_failures => true) scope = 'Scope(Class[main]):' # apply the 4x function manifest with future parser puppet_apply_options = {:modulepath => "#{testdir}/environments/production/modules/", :acceptable_exit_codes => 1} puppet_apply_options[:future_parser] = true if puppet_version =~ /\A3\./ apply_manifest_on(agent, manifest_call_each_function_from_array(functions_4x), puppet_apply_options) do |result| functions_4x.each do |function| expected = "#{function[:name].capitalize}: #{scope} #{function[:expected]}" unless agent['locale'] == 'ja' assert_match(expected, result.output, "#{function[:name]} output didn't match expected value") end end end file_path = agent.tmpfile('apply_manifest.pp') create_remote_file(agent, file_path, manifest_call_each_function_from_array(functions_3x)) trusted_3x = puppet_version =~ /\A3\./ ? '--trusted_node_data ' : '' on(agent, puppet("apply #{trusted_3x} --color=false --modulepath #{testdir}/environments/production/modules/ #{file_path}"), :acceptable_exit_codes => 1 ) do |result| functions_3x.each do |function| # append the function name to the matcher so it's more expressive if function[:expected].is_a?(String) if function[:name] == :fail expected = function[:expected] elsif function[:name] == :crit expected = "#{function[:name].capitalize}ical: #{scope} #{function[:expected]}" elsif function[:name] == :emerg expected = "#{function[:name].capitalize}ency: #{scope} #{function[:expected]}" elsif function[:name] == :err expected = "#{function[:name].capitalize}or: #{scope} #{function[:expected]}" elsif function[:expected] == '' expected = "#{function[:name].capitalize}: #{function[:expected]}" else expected = "#{function[:name].capitalize}: #{scope} #{function[:expected]}" end elsif function[:expected].is_a?(Regexp) expected = function[:expected] else raise 'unhandled function expectation type (we allow String or Regexp)' end unless agent['locale'] == 'ja' assert_match(expected, result.output, "#{function[:name]} output didn't match expected value") end end end end puppetlabs-puppet-789f600/acceptance/tests/parser_functions/hiera/000077500000000000000000000000001470131746300254035ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/parser_functions/hiera/lookup_data.rb000066400000000000000000000037361470131746300302430ustar00rootroot00000000000000test_name "Lookup data using the hiera parser function" tag 'audit:high', 'audit:acceptance', 'audit:refactor' # Master is not required for this test. Replace with agents.each testdir = master.tmpdir('hiera') step 'Setup' apply_manifest_on(master, <<-PP, :catch_failures => true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}':; '#{testdir}/hieradata':; '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/production/modules':; } file { '#{testdir}/hiera.yaml': ensure => file, content => '--- :backends: - "yaml" :logger: "console" :hierarchy: - "%{environment}" - "global" :yaml: :datadir: "#{testdir}/hieradata" ', mode => "0640", } file { '#{testdir}/hieradata/global.yaml': ensure => file, content => "--- port: 8080 ", mode => "0640", } file { '#{testdir}/environments/production/modules/apache':; '#{testdir}/environments/production/modules/apache/manifests':; } file { '#{testdir}/environments/production/modules/apache/manifests/init.pp': ensure => file, content => ' class apache { $port = hiera("port") notify { "port from hiera": message => "apache server port: ${port}" } }', mode => "0640", } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, content => " node default { include apache }", mode => "0640", } PP step "Try to lookup string data" master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", 'hiera_config' => "#{testdir}/hiera.yaml", }, } with_puppet_running_on master, master_opts, testdir do agents.each do |agent| on(agent, puppet('agent', "-t"), :acceptable_exit_codes => [2]) do |result| assert_match('apache server port: 8080', result.stdout) end end end puppetlabs-puppet-789f600/acceptance/tests/parser_functions/hiera_array/000077500000000000000000000000001470131746300266015ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/parser_functions/hiera_array/lookup_data.rb000066400000000000000000000044651470131746300314410ustar00rootroot00000000000000test_name "Lookup data using the hiera_array parser function" tag 'audit:high', 'audit:acceptance', 'audit:refactor' # Master is not required for this test. Replace with agents.each testdir = master.tmpdir('hiera') step 'Setup' apply_manifest_on(master, <<-PP, :catch_failures => true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}':; '#{testdir}/hieradata':; '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/production/modules':; } file { '#{testdir}/hiera.yaml': ensure => file, content => '--- :backends: - "yaml" :logger: "console" :hierarchy: - "%{environment}" - "global" :yaml: :datadir: "#{testdir}/hieradata" ', mode => "0640"; } file { '#{testdir}/hieradata/global.yaml': ensure => file, content => "--- port: '8080' ntpservers: ['global.ntp.puppetlabs.com'] ", mode => "0640"; } file { '#{testdir}/hieradata/production.yaml': ensure => file, content => "--- ntpservers: ['production.ntp.puppetlabs.com'] ", mode => "0640"; } file { '#{testdir}/environments/production/modules/ntp':; '#{testdir}/environments/production/modules/ntp/manifests':; } file { '#{testdir}/environments/production/modules/ntp/manifests/init.pp': ensure => file, content => ' class ntp { $ntpservers = hiera_array("ntpservers") define print { $server = $name notify { "ntpserver ${server}": } } ntp::print { $ntpservers: } }', mode => "0640"; } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, content => " node default { include ntp }", mode => "0640"; } PP step "Try to lookup array data" master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", 'hiera_config' => "#{testdir}/hiera.yaml", }, } with_puppet_running_on master, master_opts, testdir do agents.each do |agent| on(agent, puppet('agent', "-t"), :acceptable_exit_codes => [2]) do |result| assert_match('ntpserver global.ntp.puppetlabs.com', result.stdout) assert_match('ntpserver production.ntp.puppetlabs.com', result.stdout) end end end puppetlabs-puppet-789f600/acceptance/tests/parser_functions/hiera_hash/000077500000000000000000000000001470131746300264065ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/parser_functions/hiera_hash/lookup_data.rb000066400000000000000000000043301470131746300312350ustar00rootroot00000000000000test_name "Lookup data using the hiera_hash parser function" tag 'audit:high', 'audit:acceptance', 'audit:refactor' # Master is not required for this test. Replace with agents.each testdir = master.tmpdir('hiera') step 'Setup' apply_manifest_on(master, <<-PP, :catch_failures => true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{testdir}':; '#{testdir}/hieradata':; '#{testdir}/environments':; '#{testdir}/environments/production':; '#{testdir}/environments/production/manifests':; '#{testdir}/environments/production/modules':; } file { '#{testdir}/hiera.yaml': ensure => file, content => '--- :backends: - "yaml" :logger: "console" :hierarchy: - "%{environment}" - "global" :yaml: :datadir: "#{testdir}/hieradata" ', mode => "0640"; } file { '#{testdir}/hieradata/global.yaml': ensure => file, content => "--- database_user: name: postgres uid: 500 gid: 500 ", mode => "0640"; } file { '#{testdir}/hieradata/production.yaml': ensure => file, content => "--- database_user: shell: '/bin/bash' ", mode => "0640"; } file { '#{testdir}/environments/production/modules/ntp/':; '#{testdir}/environments/production/modules/ntp/manifests':; } file { '#{testdir}/environments/production/modules/ntp/manifests/init.pp': ensure => file, content => 'class ntp { $database_user = hiera_hash("database_user") notify { "the database user": message => "name: ${database_user["name"]} shell: ${database_user["shell"]}" } }', mode => "0640"; } file { '#{testdir}/environments/production/manifests/site.pp': ensure => file, content => " node default { include ntp }", mode => "0640"; } PP step "Try to lookup hash data" master_opts = { 'main' => { 'environmentpath' => "#{testdir}/environments", 'hiera_config' => "#{testdir}/hiera.yaml", }, } with_puppet_running_on master, master_opts, testdir do agents.each do |agent| on(agent, puppet('agent', "-t"), :acceptable_exit_codes => [2]) do |result| assert_match("name: postgres shell: /bin/bash", result.stdout) end end end puppetlabs-puppet-789f600/acceptance/tests/parser_functions/hiera_in_templates.rb000066400000000000000000000304611470131746300305000ustar00rootroot00000000000000test_name "Calling Hiera function from inside templates" tag 'audit:high', 'audit:integration', 'audit:refactor' # Master is not required for this test. Replace with agents.each @module_name = "hieratest" @coderoot = master.tmpdir("#{@module_name}") @msg_default = 'message from default.yaml' @msg_production = 'message from production.yaml' @msg1os = 'message1 from {osfamily}.yaml' @msg2os = 'message2 from {osfamily}.yaml' @msg_fqdn = 'messsage from {fqdn}.yaml' @k1 = 'key1' @k2 = 'key2' @k3 = 'key3' @hval2p = 'hash_value2 from production.yaml' @hval3p = 'hash_value3 from production.yaml' @hval1os = 'hash_value1 from {osfamily}.yaml' @hval2os = 'hash_value2 from {osfamily}.yaml' @h_m_call = "hiera\\('message'\\)" @h_h_call = "hiera\\('hash_value'\\)" @h_i_call = "hiera\\('includes'\\)" @ha_m_call = "hiera_array\\('message'\\)" @ha_i_call = "hiera_array\\('includes'\\)" @hh_h_call = "hiera_hash\\('hash_value'\\)" @mod_default_msg = 'This file created by mod_default.' @mod_osfamily_msg = 'This file created by mod_osfamily.' @mod_production_msg = 'This file created by mod_production.' @mod_fqdn_msg = 'This file created by mod_fqdn.' @master_opts = { 'main' => { 'environmentpath' => "#{@coderoot}/environments", 'hiera_config' => "#{@coderoot}/hiera.yaml", }, } def create_environment(osfamilies, tmp_dirs) envroot = "#{@coderoot}/environments" production = "#{envroot}/production" modroot = "#{production}/modules" moduledir = "#{modroot}/#{@module_name}" hieradir = "#{@coderoot}/hieradata" osfamily_yamls = "" osfamilies.each do |osf| new_yaml = < " --- message: [ '#{@msg1os}', '#{@msg2os}', ] includes: '#{@module_name}::mod_osfamily' hash_value: #{@k1}: '#{@hval1os}' #{@k2}: '#{@hval2os}' " } NEW_YAML osfamily_yamls += new_yaml end environ = < file, owner => #{master.puppet['user']}, group => #{master.puppet['group']}, mode => "0644", } file { [ "#{@coderoot}", "#{envroot}", "#{production}", "#{production}/modules", "#{production}/manifests", "#{hieradir}", "#{moduledir}", "#{moduledir}/examples", "#{moduledir}/manifests", "#{moduledir}/templates", ] : ensure => directory, } file { '#{production}/manifests/site.pp': ensure => file, content => " node default { \\$msgs = hiera_array('message') notify {\\$msgs:} class {'#{@module_name}': result_dir => hiera('result_dir')[\\$facts['networking']['hostname']], } } ", } file {"#{@coderoot}/hiera.yaml": content => " --- :backends: - yaml :yaml: :datadir: #{@coderoot}/hieradata :hierarchy: - \\"%{clientcert}\\" - \\"%{environment}\\" - \\"%{os.family}\\" - \\"default\\" " } file {"#{hieradir}/default.yaml": content => " --- message: '#{@msg_default}' includes: '#{@module_name}::mod_default' result_dir: #{tmp_dirs} " } #{osfamily_yamls} file {"#{hieradir}/production.yaml": content => " --- message: '#{@msg_production}' includes: '#{@module_name}::mod_production' hash_value: #{@k2}: '#{@hval2p}' #{@k3}: '#{@hval3p}' " } file {"#{hieradir}/#{$fqdn}.yaml": content => " --- message: '#{@msg_fqdn}' includes: '#{@module_name}::mod_fqdn' " } file {"#{moduledir}/examples/init.pp": content => " include #{@module_name} " } file { "#{moduledir}/manifests/init.pp": content => " class #{@module_name} ( \\$result_dir, ) { file { \\$result_dir: ensure => directory, mode => '0755', } file {\\\"\\\${result_dir}/#{@module_name}_results_epp\\\": ensure => file, mode => '0644', content => epp('#{@module_name}/hieratest_results_epp.epp'), } file {\\\"\\\${result_dir}/#{@module_name}_results_erb\\\": ensure => file, mode => '0644', content => template('#{@module_name}/hieratest_results_erb.erb'), } } " } file { "#{moduledir}/manifests/mod_default.pp": content => " class #{@module_name}::mod_default { \\$result_dir = hiera('result_dir')[\\$facts['networking']['hostname']] notify{\\"module mod_default invoked.\\\\n\\":} file {\\\"\\\${result_dir}/mod_default\\\": ensure => 'file', mode => '0644', content => \\\"#{@mod_default_msg}\\\\n\\\", } } " } file { "#{moduledir}/manifests/mod_osfamily.pp": content => " class #{@module_name}::mod_osfamily { \\$result_dir = hiera('result_dir')[\\$facts['networking']['hostname']] notify{\\"module mod_osfamily invoked.\\\\n\\":} file {\\\"\\\${result_dir}/mod_osfamily\\\": ensure => 'file', mode => '0644', content => \\\"#{@mod_osfamily_msg}\\\\n\\\", } } " } file { "#{moduledir}/manifests/mod_production.pp": content => " class #{@module_name}::mod_production { \\$result_dir = hiera('result_dir')[\\$facts['networking']['hostname']] notify{\\"module mod_production invoked.\\\\n\\":} file {\\\"\\\${result_dir}/mod_production\\\": ensure => 'file', mode => '0644', content => '#{@mod_production_msg}', } } " } file { "#{moduledir}/manifests/mod_fqdn.pp": content => " class #{@module_name}::mod_fqdn { \\$result_dir = hiera('result_dir')[\\$facts['networking']['hostname']] notify{\\"module mod_fqdn invoked.\\\\n\\":} file {\\\"\\\${result_dir}/mod_fqdn\\\": ensure => 'file', mode => '0644', content => \\\"#{@mod_fqdn_msg}\\\\n\\\", } } " } file { "#{moduledir}/templates/hieratest_results_epp.epp": content => " hiera('message'): <%= hiera('message') %> hiera('hash_value'): <%= hiera('hash_value') %> hiera('includes'): <%= hiera('includes') %> hiera_array('message'): <%= hiera_array('message') %> hiera_array('includes'): <%= hiera_array('includes') %> hiera_hash('hash_value'): <%= hiera_hash('hash_value') %> hiera_include('includes'): <%= hiera_include('includes') %> " } file { "#{moduledir}/templates/hieratest_results_erb.erb": content => " hiera('message'): <%= scope().call_function('hiera', ['message']) %> hiera('hash_value'): <%= scope().call_function('hiera', ['hash_value']) %> hiera('includes'): <%= scope().call_function('hiera', ['includes']) %> hiera_array('message'): <%= scope().call_function('hiera_array', ['message']) %> hiera_array('includes'): <%= scope().call_function('hiera_array', ['includes']) %> hiera_hash('hash_value'): <%= scope().call_function('hiera_hash', ['hash_value']) %> " } ENV environ end def find_osfamilies family_hash = {} agents.each do |agent| res = on(agent, facter("os.family")) osf = res.stdout.chomp family_hash[osf] = 1 end family_hash.keys end def find_tmp_dirs tmp_dirs = "" host_to_result_dir = {} agents.each do |agent| h = on(agent, facter("networking.hostname")).stdout.chomp t = agent.tmpdir("#{@module_name}_results") tmp_dirs += " #{h}: '#{t}'\n" host_to_result_dir[h] = t end result = { 'tmp_dirs' => tmp_dirs, 'host_to_result_dir' => host_to_result_dir } result end step 'Setup' with_puppet_running_on master, @master_opts, @coderoot do res = find_tmp_dirs tmp_dirs = res['tmp_dirs'] host_to_result_dir = res['host_to_result_dir'] env_manifest = create_environment(find_osfamilies, tmp_dirs) apply_manifest_on(master, env_manifest, :catch_failures => true) agents.each do |agent| resultdir = host_to_result_dir[on(agent, facter("networking.hostname")).stdout.chomp] step "Applying catalog to agent: #{agent}. result files in #{resultdir}" on( agent, puppet('agent', "-t"), :acceptable_exit_codes => [2] ) step "####### Verifying hiera calls from erb template #######" r1 = on(agent, "cat #{resultdir}/hieratest_results_erb") result = r1.stdout step "Verifying hiera() call #1." assert_match( /#{@h_m_call}: #{@msg_production}/, result, "#{@h_m_call} failed. Expected: '#{@msg_production}'" ) step "Verifying hiera() call #2." assert_match( /#{@h_h_call}.*\"#{@k3}\"=>\"#{@hval3p}\"/, result, "#{@h_h_call} failed. Expected: '\"#{@k3}\"=>\"#{@hval3p}\"'" ) step "Verifying hiera() call #3." assert_match( /#{@h_h_call}.*\"#{@k2}\"=>\"#{@hval2p}\"/, result, "#{@h_h_call} failed. Expected: '\"#{@k2}\"=>\"#{@hval2p}\"'" ) step "Verifying hiera() call #4." assert_match( /#{@h_i_call}: #{@module_name}::mod_production/, result, "#{@h_i_call} failed. Expected:'#{@module_name}::mod_production'" ) step "Verifying hiera_array() call. #1" assert_match( /#{@ha_m_call}: \[\"#{@msg_production}\", \"#{@msg1os}\", \"#{@msg2os}\", \"#{@msg_default}\"\]/, result, "#{@ha_m_call} failed. Expected: '[\"#{@msg_production}\", \"#{@msg1os}\", \"#{@msg2os}\", \"#{@msg_default}\"]'" ) step "Verifying hiera_array() call. #2" assert_match( /#{@ha_i_call}: \[\"#{@module_name}::mod_production\", \"#{@module_name}::mod_osfamily\", \"#{@module_name}::mod_default\"\]/, result, "#{@ha_i_call} failed. Expected: '[\"#{@module_name}::mod_production\", \"#{@module_name}::mod_osfamily\", \"#{@module_name}::mod_default\"]'" ) step "Verifying hiera_hash() call. #1" assert_match( /#{@hh_h_call}:.*\"#{@k3}\"=>\"#{@hval3p}\"/, result, "#{@hh_h_call} failed. Expected: '\"#{@k3}\"=>\"#{@hval3p}\"'" ) step "Verifying hiera_hash() call. #2" assert_match( /#{@hh_h_call}:.*\"#{@k2}\"=>\"#{@hval2p}\"/, result, "#{@hh_h_call} failed. Expected: '\"#{@k2}\"=>\"#{@hval2p}\"'" ) step "Verifying hiera_hash() call. #3" assert_match( /#{@hh_h_call}:.*\"#{@k1}\"=>\"#{@hval1os}\"/, result, "#{@hh_h_call} failed. Expected: '\"#{@k1}\"=>\"#{@hval1os}\"'" ) r2 = on(agent, "cat #{resultdir}/mod_default") result = r2.stdout step "Verifying hiera_include() call. #1" assert_match( "#{@mod_default_msg}", result, "#{@hi_i_call} failed. Expected: '#{@mod_default_msg}'" ) r3 = on(agent, "cat #{resultdir}/mod_osfamily") result = r3.stdout step "Verifying hiera_include() call. #2" assert_match( "#{@mod_osfamily_msg}", result, "#{@hi_i_call} failed. Expected: '#{@mod_osfamily_msg}'" ) r4 = on(agent, "cat #{resultdir}/mod_production") result = r4.stdout step "Verifying hiera_include() call. #3" assert_match( "#{@mod_production_msg}", result, "#{@hi_i_call} failed. Expected: '#{@mod_production_msg}'" ) step "####### Verifying hiera calls from epp template #######" r5 = on(agent, "cat #{resultdir}/hieratest_results_epp") result = r5.stdout step "Verifying hiery() call #1." assert_match( /#{@h_m_call}: #{@msg_production}/, result, "#{@hi_m_call} failed. Expected '#{@msg_production}'" ) step "Verifying hiera() call #2." assert_match( /#{@h_h_call}.*#{@k3} => #{@hval3p}/, result, "#{@h_h_call} failed. Expected '#{@k3} => #{@hval3p}'" ) step "Verifying hiera() call #3." assert_match(/#{@h_h_call}.*#{@k2} => #{@hval2p}/, result, "#{@h_h_call} failed. Expected '#{@k2} => #{@hval2p}'" ) step "Verifying hiera() call #4." assert_match( /#{@h_i_call}: #{@module_name}::mod_production/, result, "#{@h_i_call} failed. Expected: '#{@module_name}::mod_production'" ) step "Verifying hiera_array() call. #1" assert_match( /#{@ha_m_call}: \[#{@msg_production}, #{@msg1os}, #{@msg2os}, #{@msg_default}\]/, result, "#{@ha_m_call} failed. Expected: '[#{@msg_production}, #{@msg1os}, #{@msg2os}, #{@msg_default}]'" ) step "Verifying hiera_array() call. #2" assert_match( /#{@ha_i_call}: \[#{@module_name}::mod_production, #{@module_name}::mod_osfamily, #{@module_name}::mod_default\]/, result, "#{@ha_i_call} failed. Expected: '[#{@module_name}::mod_production, #{@module_name}::mod_osfamily, #{@module_name}::mod_default'" ) step "Verifying hiera_hash() call. #1" assert_match( /#{@hh_h_call}:.*#{@k3} => #{@hval3p}/, result, "#{@hh_h_call} failed. Expected: '{@k3} => #{@hval3p}'" ) step "Verifying hiera_hash() call. #2" assert_match( /#{@hh_h_call}:.*#{@k2} => #{@hval2p}/, result, "#{@hh_h_call} failed. Expected '#{@k2} => #{@hval2p}'", ) step "Verifying hiera_hash() call. #3" assert_match( /#{@hh_h_call}:.*#{@k1} => #{@hval1os}/, result, "#{@hh_h_call}: failed. Expected: '#{@k1} => #{@hval1os}'" ) end end puppetlabs-puppet-789f600/acceptance/tests/parser_functions/no_exception_in_reduce_with_bignum.rb000066400000000000000000000034121470131746300337430ustar00rootroot00000000000000test_name 'C97760: Integer in reduce() should not cause exception' do require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils tag 'audit:high', 'audit:unit' # Remove all traces of the last used environment teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end app_type = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, app_type) step 'On master, create site.pp with integer' do create_sitepp(master, tmp_environment, <<-SITEPP) $data = [ { "certname"=>"xxxxxxxxx.some.domain", "parameters"=>{ "admin_auth_keys"=>{ "keyname1"=>{ "key"=>"ABCDEF", "options"=>["from=\\"10.0.0.0/8\\""] }, "keyname2"=>{ "key"=>"ABCDEF", }, "keyname3"=>{ "key"=>"ABCDEF", "options"=>["from=\\"10.0.0.0/8\\""], "type"=>"ssh-xxx" }, "keyname4"=>{ "key"=>"ABCDEF", "options"=>["from=\\"10.0.0.0/8\\""] } }, "admin_user"=>"ertxa", "admin_hosts"=>["1.2.3.4", "1.2.3.4", "1.2.3.4"], "admin_password"=>"ABCDEF", "sshd_ports"=>[22, 22, 24], "sudo_no_password_all"=>false, "sudo_no_password_commands"=>[], "sshd_config_template"=>"cfauth/sshd_config.epp", "sudo_env_keep"=>[] }, "exported"=>false}, ] $data_reduced = $data.reduce({}) |$m, $r|{ $cn = $r['certname'] notice({ $cn => $r['parameters'] }) } SITEPP end with_puppet_running_on(master, {}) do agents.each do |agent| on(agent, puppet("agent -t --environment #{tmp_environment}")) end end end puppetlabs-puppet-789f600/acceptance/tests/parser_functions/puppet_lookup_cmd.rb000066400000000000000000002210651470131746300303770ustar00rootroot00000000000000test_name "Puppet Lookup Command" tag 'audit:high', 'audit:acceptance', 'audit:refactor' # Master is not required for this test. Replace with agents.each # Wrap steps in blocks in accordance with Beaker style guide # doc: # https://puppet.com/docs/puppet/latest/hiera_automatic.html @module_name = "puppet_lookup_command_test" ### @testroot = "/etc/puppetlabs" @testroot = master.tmpdir("#{@module_name}") @coderoot = "#{@testroot}/code" @confdir = "#{@testroot}/puppet" @node1 = 'node1.example.org' @node2 = 'node2.example.org' @master_opts = { 'main' => { 'environmentpath' => "#{@coderoot}/environments", 'hiera_config' => "#{@coderoot}/hiera.yaml", }, } @manifest = < directory, mode => "0755", } file { '#{@confdir}':; '#{@coderoot}':; '#{@coderoot}/hieradata':; '#{@coderoot}/environments':; ##### default environment, production '#{@coderoot}/environments/production':; '#{@coderoot}/environments/production/data':; '#{@coderoot}/environments/production/functions':; '#{@coderoot}/environments/production/functions/environment':; '#{@coderoot}/environments/production/lib':; '#{@coderoot}/environments/production/lib/puppet':; '#{@coderoot}/environments/production/lib/puppet/functions':; '#{@coderoot}/environments/production/lib/puppet/functions/environment':; '#{@coderoot}/environments/production/manifests':; '#{@coderoot}/environments/production/modules':; # module mod1 hiera '#{@coderoot}/environments/production/modules/mod1':; '#{@coderoot}/environments/production/modules/mod1/manifests':; '#{@coderoot}/environments/production/modules/mod1/data':; '#{@coderoot}/environments/production/modules/mod1/functions':; '#{@coderoot}/environments/production/modules/mod1/lib':; '#{@coderoot}/environments/production/modules/mod1/lib/puppet':; '#{@coderoot}/environments/production/modules/mod1/lib/puppet/functions':; '#{@coderoot}/environments/production/modules/mod1/lib/puppet/functions/mod1':; # module mod2 ruby function '#{@coderoot}/environments/production/modules/mod2':; '#{@coderoot}/environments/production/modules/mod2/manifests':; '#{@coderoot}/environments/production/modules/mod2/data':; '#{@coderoot}/environments/production/modules/mod2/functions':; '#{@coderoot}/environments/production/modules/mod2/lib':; '#{@coderoot}/environments/production/modules/mod2/lib/puppet':; '#{@coderoot}/environments/production/modules/mod2/lib/puppet/functions':; '#{@coderoot}/environments/production/modules/mod2/lib/puppet/functions/mod2':; # module mod3 puppet function '#{@coderoot}/environments/production/modules/mod3':; '#{@coderoot}/environments/production/modules/mod3/manifests':; '#{@coderoot}/environments/production/modules/mod3/data':; '#{@coderoot}/environments/production/modules/mod3/functions':; '#{@coderoot}/environments/production/modules/mod3/not-lib':; '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet':; '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet/functions':; '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet/functions/mod3':; # module mod4 none '#{@coderoot}/environments/production/modules/mod4':; '#{@coderoot}/environments/production/modules/mod4/manifests':; '#{@coderoot}/environments/production/modules/mod4/data':; '#{@coderoot}/environments/production/modules/mod4/functions':; '#{@coderoot}/environments/production/modules/mod4/lib':; '#{@coderoot}/environments/production/modules/mod4/lib/puppet':; '#{@coderoot}/environments/production/modules/mod4/lib/puppet/functions':; '#{@coderoot}/environments/production/modules/mod4/lib/puppet/functions/mod4':; ##### env1 hiera '#{@coderoot}/environments/env1':; '#{@coderoot}/environments/env1/data':; '#{@coderoot}/environments/env1/functions':; '#{@coderoot}/environments/env1/functions/environment':; '#{@coderoot}/environments/env1/lib':; '#{@coderoot}/environments/env1/lib/puppet':; '#{@coderoot}/environments/env1/lib/puppet/functions':; '#{@coderoot}/environments/env1/lib/puppet/functions/environment':; '#{@coderoot}/environments/env1/manifests':; '#{@coderoot}/environments/env1/modules':; # module mod1 hiera '#{@coderoot}/environments/env1/modules/mod1':; '#{@coderoot}/environments/env1/modules/mod1/manifests':; '#{@coderoot}/environments/env1/modules/mod1/data':; '#{@coderoot}/environments/env1/modules/mod1/functions':; '#{@coderoot}/environments/env1/modules/mod1/lib':; '#{@coderoot}/environments/env1/modules/mod1/lib/puppet':; '#{@coderoot}/environments/env1/modules/mod1/lib/puppet/functions':; '#{@coderoot}/environments/env1/modules/mod1/lib/puppet/functions/mod1':; # module mod2 ruby function '#{@coderoot}/environments/env1/modules/mod2':; '#{@coderoot}/environments/env1/modules/mod2/manifests':; '#{@coderoot}/environments/env1/modules/mod2/data':; '#{@coderoot}/environments/env1/modules/mod2/functions':; '#{@coderoot}/environments/env1/modules/mod2/lib':; '#{@coderoot}/environments/env1/modules/mod2/lib/puppet':; '#{@coderoot}/environments/env1/modules/mod2/lib/puppet/functions':; '#{@coderoot}/environments/env1/modules/mod2/lib/puppet/functions/mod2':; # module mod3 puppet function '#{@coderoot}/environments/env1/modules/mod3':; '#{@coderoot}/environments/env1/modules/mod3/manifests':; '#{@coderoot}/environments/env1/modules/mod3/data':; '#{@coderoot}/environments/env1/modules/mod3/functions':; '#{@coderoot}/environments/env1/modules/mod3/not-lib':; '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet':; '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet/functions':; '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet/functions/mod3':; # module mod4 none '#{@coderoot}/environments/env1/modules/mod4':; '#{@coderoot}/environments/env1/modules/mod4/manifests':; '#{@coderoot}/environments/env1/modules/mod4/data':; '#{@coderoot}/environments/env1/modules/mod4/functions':; '#{@coderoot}/environments/env1/modules/mod4/lib':; '#{@coderoot}/environments/env1/modules/mod4/lib/puppet':; '#{@coderoot}/environments/env1/modules/mod4/lib/puppet/functions':; '#{@coderoot}/environments/env1/modules/mod4/lib/puppet/functions/mod4':; ##### env2 ruby function '#{@coderoot}/environments/env2':; '#{@coderoot}/environments/env2/data':; '#{@coderoot}/environments/env2/functions':; '#{@coderoot}/environments/env2/functions/environment':; '#{@coderoot}/environments/env2/lib':; '#{@coderoot}/environments/env2/lib/puppet':; '#{@coderoot}/environments/env2/lib/puppet/functions':; '#{@coderoot}/environments/env2/lib/puppet/functions/environment':; '#{@coderoot}/environments/env2/manifests':; '#{@coderoot}/environments/env2/modules':; # module mod1 hiera '#{@coderoot}/environments/env2/modules/mod1':; '#{@coderoot}/environments/env2/modules/mod1/manifests':; '#{@coderoot}/environments/env2/modules/mod1/data':; '#{@coderoot}/environments/env2/modules/mod1/functions':; '#{@coderoot}/environments/env2/modules/mod1/lib':; '#{@coderoot}/environments/env2/modules/mod1/lib/puppet':; '#{@coderoot}/environments/env2/modules/mod1/lib/puppet/functions':; '#{@coderoot}/environments/env2/modules/mod1/lib/puppet/functions/mod1':; # module mod2 ruby function '#{@coderoot}/environments/env2/modules/mod2':; '#{@coderoot}/environments/env2/modules/mod2/manifests':; '#{@coderoot}/environments/env2/modules/mod2/data':; '#{@coderoot}/environments/env2/modules/mod2/functions':; '#{@coderoot}/environments/env2/modules/mod2/lib':; '#{@coderoot}/environments/env2/modules/mod2/lib/puppet':; '#{@coderoot}/environments/env2/modules/mod2/lib/puppet/functions':; '#{@coderoot}/environments/env2/modules/mod2/lib/puppet/functions/mod2':; # module mod3 puppet function '#{@coderoot}/environments/env2/modules/mod3':; '#{@coderoot}/environments/env2/modules/mod3/manifests':; '#{@coderoot}/environments/env2/modules/mod3/data':; '#{@coderoot}/environments/env2/modules/mod3/functions':; '#{@coderoot}/environments/env2/modules/mod3/not-lib':; '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet':; '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet/functions':; '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet/functions/mod3':; # module mod4 none '#{@coderoot}/environments/env2/modules/mod4':; '#{@coderoot}/environments/env2/modules/mod4/manifests':; '#{@coderoot}/environments/env2/modules/mod4/data':; '#{@coderoot}/environments/env2/modules/mod4/functions':; '#{@coderoot}/environments/env2/modules/mod4/lib':; '#{@coderoot}/environments/env2/modules/mod4/lib/puppet':; '#{@coderoot}/environments/env2/modules/mod4/lib/puppet/functions':; '#{@coderoot}/environments/env2/modules/mod4/lib/puppet/functions/mod4':; ##### env3 puppet function '#{@coderoot}/environments/env3':; '#{@coderoot}/environments/env3/data':; '#{@coderoot}/environments/env3/functions':; '#{@coderoot}/environments/env3/functions/environment':; '#{@coderoot}/environments/env3/not-lib':; '#{@coderoot}/environments/env3/not-lib/puppet':; '#{@coderoot}/environments/env3/not-lib/puppet/functions':; '#{@coderoot}/environments/env3/not-lib/puppet/functions/environment':; '#{@coderoot}/environments/env3/manifests':; '#{@coderoot}/environments/env3/modules':; # module mod1 hiera '#{@coderoot}/environments/env3/modules/mod1':; '#{@coderoot}/environments/env3/modules/mod1/manifests':; '#{@coderoot}/environments/env3/modules/mod1/data':; '#{@coderoot}/environments/env3/modules/mod1/functions':; '#{@coderoot}/environments/env3/modules/mod1/lib':; '#{@coderoot}/environments/env3/modules/mod1/lib/puppet':; '#{@coderoot}/environments/env3/modules/mod1/lib/puppet/functions':; '#{@coderoot}/environments/env3/modules/mod1/lib/puppet/functions/mod1':; # module mod2 ruby function '#{@coderoot}/environments/env3/modules/mod2':; '#{@coderoot}/environments/env3/modules/mod2/manifests':; '#{@coderoot}/environments/env3/modules/mod2/data':; '#{@coderoot}/environments/env3/modules/mod2/functions':; '#{@coderoot}/environments/env3/modules/mod2/lib':; '#{@coderoot}/environments/env3/modules/mod2/lib/puppet':; '#{@coderoot}/environments/env3/modules/mod2/lib/puppet/functions':; '#{@coderoot}/environments/env3/modules/mod2/lib/puppet/functions/mod2':; # module mod3 puppet function '#{@coderoot}/environments/env3/modules/mod3':; '#{@coderoot}/environments/env3/modules/mod3/manifests':; '#{@coderoot}/environments/env3/modules/mod3/data':; '#{@coderoot}/environments/env3/modules/mod3/functions':; '#{@coderoot}/environments/env3/modules/mod3/not-lib':; '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet':; '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet/functions':; '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet/functions/mod3':; # module mod4 none '#{@coderoot}/environments/env3/modules/mod4':; '#{@coderoot}/environments/env3/modules/mod4/manifests':; '#{@coderoot}/environments/env3/modules/mod4/data':; '#{@coderoot}/environments/env3/modules/mod4/functions':; '#{@coderoot}/environments/env3/modules/mod4/lib':; '#{@coderoot}/environments/env3/modules/mod4/lib/puppet':; '#{@coderoot}/environments/env3/modules/mod4/lib/puppet/functions':; '#{@coderoot}/environments/env3/modules/mod4/lib/puppet/functions/mod4':; ##### env4 none '#{@coderoot}/environments/env4':; '#{@coderoot}/environments/env4/data':; '#{@coderoot}/environments/env4/functions':; '#{@coderoot}/environments/env4/functions/environment':; '#{@coderoot}/environments/env4/lib':; '#{@coderoot}/environments/env4/lib/puppet':; '#{@coderoot}/environments/env4/lib/puppet/functions':; '#{@coderoot}/environments/env4/lib/puppet/functions/environment':; '#{@coderoot}/environments/env4/manifests':; '#{@coderoot}/environments/env4/modules':; # module mod1 hiera '#{@coderoot}/environments/env4/modules/mod1':; '#{@coderoot}/environments/env4/modules/mod1/manifests':; '#{@coderoot}/environments/env4/modules/mod1/data':; '#{@coderoot}/environments/env4/modules/mod1/functions':; '#{@coderoot}/environments/env4/modules/mod1/lib':; '#{@coderoot}/environments/env4/modules/mod1/lib/puppet':; '#{@coderoot}/environments/env4/modules/mod1/lib/puppet/functions':; '#{@coderoot}/environments/env4/modules/mod1/lib/puppet/functions/mod1':; # module mod2 ruby function '#{@coderoot}/environments/env4/modules/mod2':; '#{@coderoot}/environments/env4/modules/mod2/manifests':; '#{@coderoot}/environments/env4/modules/mod2/data':; '#{@coderoot}/environments/env4/modules/mod2/functions':; '#{@coderoot}/environments/env4/modules/mod2/lib':; '#{@coderoot}/environments/env4/modules/mod2/lib/puppet':; '#{@coderoot}/environments/env4/modules/mod2/lib/puppet/functions':; '#{@coderoot}/environments/env4/modules/mod2/lib/puppet/functions/mod2':; # module mod3 puppet function '#{@coderoot}/environments/env4/modules/mod3':; '#{@coderoot}/environments/env4/modules/mod3/manifests':; '#{@coderoot}/environments/env4/modules/mod3/data':; '#{@coderoot}/environments/env4/modules/mod3/functions':; '#{@coderoot}/environments/env4/modules/mod3/not-lib':; '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet':; '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet/functions':; '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet/functions/mod3':; # module mod4 none '#{@coderoot}/environments/env4/modules/mod4':; '#{@coderoot}/environments/env4/modules/mod4/manifests':; '#{@coderoot}/environments/env4/modules/mod4/data':; '#{@coderoot}/environments/env4/modules/mod4/functions':; '#{@coderoot}/environments/env4/modules/mod4/lib':; '#{@coderoot}/environments/env4/modules/mod4/lib/puppet':; '#{@coderoot}/environments/env4/modules/mod4/lib/puppet/functions':; '#{@coderoot}/environments/env4/modules/mod4/lib/puppet/functions/mod4':; } ## Global data provider config (hiera) file { '#{@coderoot}/hiera.yaml': ensure => file, mode => "0644", content => '--- :backends: - "yaml" :logger: "console" :hierarchy: - "global" :yaml: :datadir: "#{@coderoot}/hieradata" ', } ## facts file file { '#{@coderoot}/facts.yaml': ensure => file, mode => "0644", content => '--- my_data_key: "my_data_value" ', } file { '#{@coderoot}/hieradata/global.yaml': ensure => file, mode => "0644", content => '--- global_key: "global-hiera provided value for key" another_global_key: "global-hiera provided value for key" mod1::global_key: "global-hiera provided value for key" mod2::global_key: "global-hiera provided value for key" mod3::global_key: "global-hiera provided value for key" mod4::global_key: "global-hiera provided value for key" ', } ## Evironment data provider configuration file { '#{@coderoot}/environments/production/environment.conf': ensure => file, mode => "0644", content => 'environment_timeout = 0 ', } file { '#{@coderoot}/environments/env1/environment.conf': ensure => file, mode => "0644", content => 'environment_timeout = 0 environment_data_provider = "hiera" ', } file { '#{@coderoot}/environments/env2/environment.conf': ensure => file, mode => "0644", content => 'environment_timeout = 0 environment_data_provider = "function" ', } file { '#{@coderoot}/environments/env3/environment.conf': ensure => file, mode => "0644", content => 'environment_timeout = 0 environment_data_provider = "function" ', } file { '#{@coderoot}/environments/env4/environment.conf': ensure => file, mode => "0644", content => 'environment_timeout = 0 environment_data_provider = "none" ', } # Environment hiera data provider file { '#{@coderoot}/environments/production/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/production/data/common.yaml': ensure => file, mode => "0644", content => '--- global_key: "env-production hiera provided value" environment_key: "env-production hiera provided value" ', } file { '#{@coderoot}/environments/env1/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env1/data/common.yaml': ensure => file, mode => "0644", content => '--- global_key: "env-env1 hiera provided value" environment_key: "env-env1 hiera provided value" ', } file { '#{@coderoot}/environments/env2/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env2/data/common.yaml': ensure => file, mode => "0644", content => '--- global_key: "env-env1 hiera provided value" environment_key: "env-env1 hiera provided value" ', } file { '#{@coderoot}/environments/env3/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env3/data/common.yaml': ensure => file, mode => "0644", content => '--- global_key: "env-env1 hiera provided value" environment_key: "env-env1 hiera provided value" ', } file { '#{@coderoot}/environments/env4/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env4/data/common.yaml': ensure => file, mode => "0644", content => '--- global_key: "env-env1 hiera provided value" environment_key: "env-env1 hiera provided value" ', } # Environment ruby function data provider file { '#{@coderoot}/environments/production/lib/puppet/functions/environment/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'environment::data') do def data() { 'environment_key': 'env-production-ruby-function data() provided value', 'global_key': 'env-production-ruby-function data () provided value', } end end ", } file { '#{@coderoot}/environments/env1/lib/puppet/functions/environment/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'environment::data') do def data() { 'environment_key' => 'env-env1-ruby-function data() provided value', 'global_key' => 'env-env1-ruby-function data () provided value', } end end ", } file { '#{@coderoot}/environments/env2/lib/puppet/functions/environment/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'environment::data') do def data() { 'environment_key' => 'env-env2-ruby-function data() provided value', 'global_key' => 'env-env2-ruby-function data () provided value', } end end ", } file { '#{@coderoot}/environments/env3/not-lib/puppet/functions/environment/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'environment::data') do def data() { 'environment_key' => 'env-env3-ruby-function data() provided value', 'global_key' => 'env-env3-ruby-function data () provided value', } end end ", } file { '#{@coderoot}/environments/env4/lib/puppet/functions/environment/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'environment::data') do def data() { 'environment_key' => 'env-env4-ruby-function data() provided value', 'global_key' => 'env-env4-ruby-function data () provided value', } end end ", } # Environment puppet function data provider file { '#{@coderoot}/environments/production/functions/environment/data.pp': ensure => file, mode => "0755", content => 'function environment::data() { { "environment_key" => "env-production-puppet-function data() provided value", "global_key" => "env-production-puppet-function data() provided value", } } ', } file { '#{@coderoot}/environments/env1/functions/environment/data.pp': ensure => file, mode => "0755", content => 'function environment::data() { { "environment_key" => "env-env1-puppet-function data() provided value", "global_key" => "env-env1-puppet-function data() provided value", } } ', } file { '#{@coderoot}/environments/env2/functions/environment/data.pp': ensure => file, mode => "0755", content => 'function environment::data() { { "environment_key" => "env-env2-puppet-function data() provided value", "global_key" => "env-env2-puppet-function data() provided value", } } ', } file { '#{@coderoot}/environments/env3/functions/environment/data.pp': ensure => file, mode => "0755", content => 'function environment::data() { { "environment_key" => "env-env3-puppet-function data() provided value", "global_key" => "env-env3-puppet-function data() provided value", } } ', } file { '#{@coderoot}/environments/env4/functions/environment/data.pp': ensure => file, mode => "0755", content => 'function environment::data() { { "environment_key" => "env-env4-puppet-function data() provided value", "global_key" => "env-env4-puppet-function data() provided value", } } ', } ## Module data provider configuration # Module hiera data provider file { '#{@coderoot}/environments/production/modules/mod1/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/production/modules/mod1/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod1::module_key": "module-production-mod1-hiera provided value" "mod1::global_key": "module-production-mod1-hiera provided value" "environment_key": "module-production-mod1-hiera provided value" "global_key": "module-production-mod1-hiera provided value" ', } file { '#{@coderoot}/environments/production/modules/mod2/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/production/modules/mod2/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod2::module_key": "module-production-mod2-hiera provided value" "mod2::global_key": "module-production-mod2-hiera provided value" "environment_key": "module-production-mod2-hiera provided value" "global_key": "module-production-mod2-hiera provided value" ', } file { '#{@coderoot}/environments/production/modules/mod3/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/production/modules/mod3/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod3::module_key": "module-production-mod3-hiera provided value" "mod3::global_key": "module-production-mod3-hiera provided value" "environment_key": "module-production-mod3-hiera provided value" "global_key" => "module-production-mod3-hiera provided value" ', } file { '#{@coderoot}/environments/production/modules/mod4/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/production/modules/mod4/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod4::module_key": "module-production-mod4-hiera provided value" "mod4::global_key": "module-production-mod4-hiera provided value" "environment_key": "module-production-mod4-hiera provided value" "global_key": "module-production-mod4-hiera provided value" ', } file { '#{@coderoot}/environments/env1/modules/mod1/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env1/modules/mod1/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod1::module_key": "module-env1-mod1-hiera provided value" "global_key": "module-env1-mod1-hiera provided value" "environment_key": "module-env1-mod1-hiera provided value" ', } file { '#{@coderoot}/environments/env1/modules/mod2/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env1/modules/mod2/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod2::module_key": "module-env1-mod2-hiera provided value" "global_key": "module-env1-mod2-hiera provided value" "environment_key": "module-env1-mod2-hiera provided value" ', } file { '#{@coderoot}/environments/env1/modules/mod3/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env1/modules/mod3/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod3::module_key": "module-env1-mod3-hiera provided value" "global_key": "module-env1-mod3-hiera provided value" "environment_key": "module-env1-mod3-hiera provided value" ', } file { '#{@coderoot}/environments/env1/modules/mod4/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env1/modules/mod4/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod4::module_key": "module-env1-mod4-hiera provided value" "global_key": "module-env1-mod4-hiera provided value" "environment_key": "module-env1-mod4-hiera provided value" ', } file { '#{@coderoot}/environments/env2/modules/mod1/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env2/modules/mod1/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod1::module_key": "module-env2-mod1-hiera provided value" "global_key": "module-env2-mod1-hiera provided value" "environment_key": "module-env2-mod1-hiera provided value" ', } file { '#{@coderoot}/environments/env2/modules/mod2/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env2/modules/mod2/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod2::module_key": "module-env2-mod2-hiera provided value" "global_key": "module-env2-mod2-hiera provided value" "environment_key": "module-env2-mod2-hiera provided value" ', } file { '#{@coderoot}/environments/env2/modules/mod3/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env2/modules/mod3/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod3::module_key": "module-env2-mod3-hiera provided value" "global_key": "module-env2-mod3-hiera provided value" "environment_key": "module-env2-mod3-hiera provided value" ', } file { '#{@coderoot}/environments/env2/modules/mod4/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env2/modules/mod4/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod4::module_key": "module-env2-mod4-hiera provided value" "global_key": "module-env2-mod4-hiera provided value" "environment_key": "module-env2-mod4-hiera provided value" ', } file { '#{@coderoot}/environments/env3/modules/mod1/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env3/modules/mod1/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod1::module_key": "module-env3-mod1-hiera provided value" "global_key": "module-env3-mod1-hiera provided value" "environment_key": "module-env3-mod1-hiera provided value" ', } file { '#{@coderoot}/environments/env3/modules/mod2/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env3/modules/mod2/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod2::module_key": "module-env3-mod2-hiera provided value" "global_key": "module-env3-mod2-hiera provided value" "environment_key": "module-env3-mod2-hiera provided value" ', } file { '#{@coderoot}/environments/env3/modules/mod3/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env3/modules/mod3/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod3::module_key": "module-env3-mod3-hiera provided value" "global_key": "module-env3-mod3-hiera provided value" "environment_key": "module-env3-mod3-hiera provided value" ', } file { '#{@coderoot}/environments/env3/modules/mod4/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env3/modules/mod4/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod4::module_key": "module-env3-mod4-hiera provided value" "global_key": "module-env3-mod4-hiera provided value" "environment_key": "module-env3-mod4-hiera provided value" ', } file { '#{@coderoot}/environments/env4/modules/mod1/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env4/modules/mod1/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod1::module_key": "module-env4-mod1-hiera provided value" "global_key": "module-env4-mod1-hiera provided value" "environment_key": "module-env4-mod1-hiera provided value" ', } file { '#{@coderoot}/environments/env4/modules/mod2/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env4/modules/mod2/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod2::module_key": "module-env4-mod2-hiera provided value" "global_key": "module-env4-mod2-hiera provided value" "environment_key": "module-env4-mod2-hiera provided value" ', } file { '#{@coderoot}/environments/env4/modules/mod3/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env4/modules/mod3/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod3::module_key": "module-env4-mod3-hiera provided value" "global_key": "module-env4-mod3-hiera provided value" "environment_key": "module-env4-mod3-hiera provided value" ', } file { '#{@coderoot}/environments/env4/modules/mod4/hiera.yaml': ensure => file, mode => "0644", content => '--- version: 4 ', } file { '#{@coderoot}/environments/env4/modules/mod4/data/common.yaml': ensure => file, mode => "0644", content => '--- "mod4::module_key": "module-env4-mod4-hiera provided value" "global_key": "module-env4-mod4-hiera provided value" "environment_key": "module-env4-mod4-hiera provided value" ', } # Module ruby function data provider file { '#{@coderoot}/environments/production/modules/mod1/lib/puppet/functions/mod1/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod1::data') do def data() { 'mod1::module_key' => 'module-production-mod1-ruby-function provided value', 'mod1::global_key' => 'module-production-mod1-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/production/modules/mod2/lib/puppet/functions/mod2/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod2::data') do def data() { 'mod2::module_key' => 'module-production-mod2-ruby-function provided value', 'mod2::global_key' => 'module-production-mod2-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/production/modules/mod3/not-lib/puppet/functions/mod3/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod3::data') do def data() { 'mod3::module_key' => 'module-production-mod3-ruby-function provided value', 'mod3::global_key' => 'module-production-mod3-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/production/modules/mod4/lib/puppet/functions/mod4/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod4::data') do def data() { 'mod4::module_key' => 'module-production-mod4-ruby-function provided value', 'mod4::global_key' => 'module-production-mod4-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env1/modules/mod1/lib/puppet/functions/mod1/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod1::data') do def data() { 'mod1::module_key' => 'module-env1-mod1-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env1/modules/mod2/lib/puppet/functions/mod2/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod2::data') do def data() { 'mod2::module_key' => 'module-env1-mod2-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env1/modules/mod3/not-lib/puppet/functions/mod3/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod3::data') do def data() { 'mod3::module_key' => 'module-env1-mod3-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env1/modules/mod4/lib/puppet/functions/mod4/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod4::data') do def data() { 'mod4::module_key' => 'module-env1-mod4-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env2/modules/mod1/lib/puppet/functions/mod1/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod1::data') do def data() { 'mod1::module_key' => 'module-env2-mod1-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env2/modules/mod2/lib/puppet/functions/mod2/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod2::data') do def data() { 'mod2::module_key' => 'module-env2-mod2-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env2/modules/mod3/not-lib/puppet/functions/mod3/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod3::data') do def data() { 'mod3::module_key' => 'module-env2-mod3-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env2/modules/mod4/lib/puppet/functions/mod4/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod4::data') do def data() { 'mod4::module_key' => 'module-env2-mod4-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env3/modules/mod1/lib/puppet/functions/mod1/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod1::data') do def data() { 'mod1::module_key' => 'module-env3-mod1-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env3/modules/mod2/lib/puppet/functions/mod2/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod2::data') do def data() { 'mod2::module_key' => 'module-env3-mod2-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env3/modules/mod3/not-lib/puppet/functions/mod3/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod3::data') do def data() { 'mod3::module_key' => 'module-env3-mod3-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env3/modules/mod4/lib/puppet/functions/mod4/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod4::data') do def data() { 'mod4::module_key' => 'module-env3-mod4-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env4/modules/mod1/lib/puppet/functions/mod1/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod1::data') do def data() { 'mod1::module_key' => 'module-env4-mod1-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env4/modules/mod2/lib/puppet/functions/mod2/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod2::data') do def data() { 'mod2::module_key' => 'module-env4-mod2-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env4/modules/mod3/not-lib/puppet/functions/mod3/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod3::data') do def data() { 'mod3::module_key' => 'module-env4-mod3-ruby-function provided value', } end end ", } file { '#{@coderoot}/environments/env4/modules/mod4/lib/puppet/functions/mod4/data.rb': ensure => file, mode => "0644", content => "Puppet::Functions.create_function(:'mod4::data') do def data() { 'mod4::module_key' => 'module-env4-mod4-ruby-function provided value', } end end ", } # Module puppet function data provider file { '#{@coderoot}/environments/production/modules/mod1/functions/data.pp': ensure => file, mode => "0644", content => "function mod1::data() { { 'mod1::module_key' => 'module-production-mod1-puppet-function provided value', 'mod1::global_key' => 'module-production-mod1-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/production/modules/mod2/functions/data.pp': ensure => file, mode => "0644", content => "function mod2::data() { { 'mod2::module_key' => 'module-production-mod2-puppet-function provided value', 'mod2::global_key' => 'module-production-mod2-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/production/modules/mod3/functions/data.pp': ensure => file, mode => "0644", content => "function mod3::data() { { 'mod3::module_key' => 'module-production-mod3-puppet-function provided value', 'mod3::global_key' => 'module-production-mod3-puppet-funtion provided value', } } ", } file { '#{@coderoot}/environments/production/modules/mod4/functions/data.pp': ensure => file, mode => "0644", content => "function mod4::data() { { 'mod4::module_key' => 'module-production-mod4-puppet-function provided value', 'mod4::global_key' => 'module-production-mod4-puppet-funtion provided value', } } ", } file { '#{@coderoot}/environments/env1/modules/mod1/functions/data.pp': ensure => file, mode => "0644", content => "function mod1::data() { { 'mod1::module_key' => 'module-env1-mod1-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env1/modules/mod2/functions/data.pp': ensure => file, mode => "0644", content => "function mod2::data() { { 'mod2::module_key' => 'module-env1-mod2-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env1/modules/mod3/functions/data.pp': ensure => file, mode => "0644", content => "function mod3::data() { { 'mod3::module_key' => 'module-env1-mod3-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env1/modules/mod4/functions/data.pp': ensure => file, mode => "0644", content => "function mod4::data() { { 'mod4::module_key' => 'module-env1-mod4-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env2/modules/mod1/functions/data.pp': ensure => file, mode => "0644", content => "function mod1::data() { { 'mod1::module_key' => 'module-env2-mod1-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env2/modules/mod2/functions/data.pp': ensure => file, mode => "0644", content => "function mod2::data() { { 'mod2::module_key' => 'module-env2-mod2-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env2/modules/mod3/functions/data.pp': ensure => file, mode => "0644", content => "function mod3::data() { { 'mod3::module_key' => 'module-env2-mod3-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env2/modules/mod4/functions/data.pp': ensure => file, mode => "0644", content => "function mod4::data() { { 'mod4::module_key' => 'module-env2-mod4-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env3/modules/mod1/functions/data.pp': ensure => file, mode => "0644", content => "function mod1::data() { { 'mod1::module_key' => 'module-env3-mod1-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env3/modules/mod2/functions/data.pp': ensure => file, mode => "0644", content => "function mod2::data() { { 'mod2::module_key' => 'module-env3-mod2-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env3/modules/mod3/functions/data.pp': ensure => file, mode => "0644", content => "function mod3::data() { { 'mod3::module_key' => 'module-env3-mod3-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env3/modules/mod4/functions/data.pp': ensure => file, mode => "0644", content => "function mod4::data() { { 'mod4::module_key' => 'module-env3-mod4-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env4/modules/mod1/functions/data.pp': ensure => file, mode => "0644", content => "function mod1::data() { { 'mod1::module_key' => 'module-env4-mod1-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env4/modules/mod2/functions/data.pp': ensure => file, mode => "0644", content => "function mod2::data() { { 'mod2::module_key' => 'module-env4-mod2-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env4/modules/mod3/functions/data.pp': ensure => file, mode => "0644", content => "function mod3::data() { { 'mod3::module_key' => 'module-env4-mod3-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/env4/modules/mod4/functions/data.pp': ensure => file, mode => "0644", content => "function mod4::data() { { 'mod4::module_key' => 'module-env4-mod4-puppet-function provided value', } } ", } file { '#{@coderoot}/environments/production/manifests/site.pp': ensure => file, mode => "0644", content => "node default { include mod1 include mod2 include mod3 include mod4 } ", } file { '#{@coderoot}/environments/env1/manifests/site.pp': ensure => file, mode => "0644", content => "node default { include mod1 include mod2 include mod3 include mod4 } ", } file { '#{@coderoot}/environments/env2/manifests/site.pp': ensure => file, mode => "0644", content => "node default { include mod1 include mod2 include mod3 include mod4 } ", } file { '#{@coderoot}/environments/env3/manifests/site.pp': ensure => file, mode => "0644", content => "node default { include mod1 include mod2 include mod3 include mod4 } ", } file { '#{@coderoot}/environments/env4/manifests/site.pp': ensure => file, mode => "0644", content => "node default { include mod1 include mod2 include mod2 include mod2 } ", } file { '#{@coderoot}/environments/production/modules/mod1/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod1 { notice("hello from production-mod1") } ', } file { '#{@coderoot}/environments/production/modules/mod2/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod2 { notice("hello from production-mod2") } ', } file { '#{@coderoot}/environments/production/modules/mod3/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod3 { notice("hello from production-mod3") } ', } file { '#{@coderoot}/environments/production/modules/mod4/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod4 { notice("hello from production-mod4") } ', } file { '#{@coderoot}/environments/env1/modules/mod1/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod1 { notice("hello from env1-mod1") } ', } file { '#{@coderoot}/environments/env1/modules/mod2/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod2 { notice("hello from env1-mod2") } ', } file { '#{@coderoot}/environments/env1/modules/mod3/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod3 { notice("hello from env1-mod3") } ', } file { '#{@coderoot}/environments/env1/modules/mod4/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod4 { notice("hello from env1-mod4") } ', } file { '#{@coderoot}/environments/env2/modules/mod1/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod1 { notice("hello from env2-mod1") } ', } file { '#{@coderoot}/environments/env2/modules/mod2/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod2 { notice("hello from env2-mod2") } ', } file { '#{@coderoot}/environments/env2/modules/mod3/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod3 { notice("hello from env2-mod3") } ', } file { '#{@coderoot}/environments/env2/modules/mod4/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod4 { notice("hello from env2-mod4") } ', } file { '#{@coderoot}/environments/env3/modules/mod1/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod1 { notice("hello from env3-mod1") } ', } file { '#{@coderoot}/environments/env3/modules/mod2/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod2 { notice("hello from env3-mod2") } ', } file { '#{@coderoot}/environments/env3/modules/mod3/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod3 { notice("hello from env3-mod3") } ', } file { '#{@coderoot}/environments/env3/modules/mod4/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod4 { notice("hello from env3-mod4") } ', } file { '#{@coderoot}/environments/env4/modules/mod1/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod1 { notice("hello from env4-mod1") } ', } file { '#{@coderoot}/environments/env4/modules/mod2/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod2 { notice("hello from env4-mod2") } ', } file { '#{@coderoot}/environments/env4/modules/mod3/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod3 { notice("hello from env4-mod3") } ', } file { '#{@coderoot}/environments/env4/modules/mod4/manifests/init.pp': ensure => file, mode => "0644", content => 'class mod4 { notice("hello from env4-mod4") } ', } file { '#{@coderoot}/environments/production/modules/mod1/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod1", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "hiera" } ', } file { '#{@coderoot}/environments/production/modules/mod2/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod2", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/production/modules/mod3/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod3", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/production/modules/mod4/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod1", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "none" } ', } file { '#{@coderoot}/environments/env1/modules/mod1/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod1", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "hiera" } ', } file { '#{@coderoot}/environments/env1/modules/mod2/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod2", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env1/modules/mod3/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod3", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env1/modules/mod4/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod4", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "none" } ', } file { '#{@coderoot}/environments/env2/modules/mod1/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod1", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "hiera" } ', } file { '#{@coderoot}/environments/env2/modules/mod2/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod2", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env2/modules/mod3/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod3", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env2/modules/mod4/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod4", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "none" } ', } file { '#{@coderoot}/environments/env3/modules/mod1/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod1", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "hiera" } ', } file { '#{@coderoot}/environments/env3/modules/mod2/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod2", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env3/modules/mod3/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod3", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env3/modules/mod4/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod4", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "none" } ', } file { '#{@coderoot}/environments/env4/modules/mod1/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod1", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "hiera" } ', } file { '#{@coderoot}/environments/env4/modules/mod2/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod2", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env4/modules/mod3/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod3", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "function" } ', } file { '#{@coderoot}/environments/env4/modules/mod4/metadata.json': ensure => file, mode => "0644", content => '{ "name": "tester-mod4", "version": "0.1.0", "author": "tester", "summary": null, "license": "Apache-2.0", "source": "", "project_page": null, "issues_url": null, "dependencies": [], "data_provider": "none" } ', } MANIFEST @env1puppetconfmanifest = < file, mode => "0664", content => "[server] vardir = /opt/puppetlabs/server/data/puppetserver logdir = /var/log/puppetlabs/puppetserver rundir = /var/run/puppetlabs/puppetserver pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid codedir = #{@coderoot} [main] environmentpath = #{@coderoot}/environments hiera_config = #{@coderoot}/hiera.yaml environment = env1 server = #{master.connection.hostname} ", } MANI1 @env2puppetconfmanifest = < file, mode => "0664", content => "[server] vardir = /opt/puppetlabs/server/data/puppetserver logdir = /var/log/puppetlabs/puppetserver rundir = /var/run/puppetlabs/puppetserver pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid codedir = #{@coderoot} [main] environmentpath = #{@coderoot}/environments hiera_config = #{@coderoot}/hiera.yaml environment = env2 server = #{master.connection.hostname} ", } MANI2 @env3puppetconfmanifest = < file, mode => "0664", content => "[server] vardir = /opt/puppetlabs/server/data/puppetserver logdir = /var/log/puppetlabs/puppetserver rundir = /var/run/puppetlabs/puppetserver pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid codedir = #{@coderoot} [main] environmentpath = #{@coderoot}/environments hiera_config = #{@coderoot}/hiera.yaml environment = env3 server = #{master.connection.hostname} ", } MANI3 @env4puppetconfmanifest = < file, mode => "0664", content => "[server] vardir = /opt/puppetlabs/server/data/puppetserver logdir = /var/log/puppetlabs/puppetserver rundir = /var/run/puppetlabs/puppetserver pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid codedir = #{@coderoot} [main] environmentpath = #{@coderoot}/environments hiera_config = #{@coderoot}/hiera.yaml environment = env4 server = #{master.connection.hostname} ", } MANI4 @encmanifest = < file, mode => "0755", content => "#!#{master['privatebindir']}/ruby nodename = ARGV.shift node2env = { '#{@node1}' => \\\"---\\\\n environment: env2\\\\n\\\", '#{@node2}' => \\\"---\\\\n environment: env3\\\\n\\\", } puts (\\\"\#{node2env[nodename]}\\\" ||'') ", } file { '#{@confdir}/puppet.conf' : ensure => file, mode => "0664", content => "[server] vardir = /opt/puppetlabs/server/data/puppetserver logdir = /var/log/puppetlabs/puppetserver rundir = /var/run/puppetlabs/puppetserver pidfile = /var/run/puppetlabs/puppetserver/puppetserver.pid codedir = #{@coderoot} [server] node_terminus = exec external_nodes = #{@coderoot}/enc.rb [main] environmentpath = #{@coderoot}/environments hiera_config = #{@coderoot}/hiera.yaml server = #{master.connection.hostname} ", } MANIENC teardown do on(master, "rm -f /etc/puppetlabs/puppet/ssl/certs/#{@node1}.pem") on(master, "rm -f /etc/puppetlabs/puppet/ssl/certs/#{@node2}.pem") on(master, "rm -f /etc/puppetlabs/puppet/ssl/ca/signed/#{@node1}.pem") on(master, "rm -f /etc/puppetlabs/puppet/ssl/ca/signed/#{@node2}.pem") on(master, "rm -f /etc/puppetlabs/puppet/ssl/private_keys/#{@node1}.pem") on(master, "rm -f /etc/puppetlabs/puppet/ssl/private_keys/#{@node2}.pem") on(master, "rm -f /etc/puppetlabs/puppet/ssl/public_keys/#{@node1}.pem") on(master, "rm -f /etc/puppetlabs/puppet/ssl/public_keys/#{@node2}.pem") end step 'apply main manifest' apply_manifest_on(master, @manifest, :catch_failures => true) step 'start puppet server' with_puppet_running_on master, @master_opts, @coderoot do step "global_key" rg = on(master, puppet('lookup', 'global_key')) result = rg.stdout assert_match( /global-hiera/, result, "global_key lookup failed, expected 'global-hiera'" ) step "production environment_key not provided" on(master, puppet('lookup', 'enviroment_key'), :acceptable_exit_codes => [1]) step "environment_key from environment env1" re1 = on(master, puppet('lookup', '--environment env1', 'environment_key')) result = re1.stdout assert_match( /env-env1 hiera/, result, "env1 environment_key lookup failed, expected 'env-env1 hiera'" ) step "environment_key from environment env2" re2 = on(master, puppet('lookup', '--environment env2', 'environment_key')) result = re2.stdout assert_match( /env-env2-ruby-function/, result, "env2 environment_key lookup failed, expected 'env-env2-puppet-function'" ) step "environment_key from environment env3" re3 = on(master, puppet('lookup', '--environment env3', 'environment_key')) result = re3.stdout assert_match( /env-env3-puppet-function/, result, "env3 environment_key lookup failed, expected 'env-env2-ruby-function data() provided value'" ) step "environment_key from environment env4" on(master, puppet('lookup', '--environment env4', 'environment_key'), :acceptable_exit_codes => [1]) step "production mod1 module_key" repm1 = on(master, puppet('lookup', 'mod1::module_key')) result = repm1.stdout assert_match( /module-production-mod1-hiera/, result, "production mod1 module_key lookup failed, expected 'module-production-mod1-hiera'" ) step "production mod2 module_key" repm2 = on(master, puppet('lookup', 'mod2::module_key')) result = repm2.stdout assert_match( /module-production-mod2-ruby-function/, result, "production mod2 module_key lookup failed, expected 'module-production-mod2-ruby-function'" ) step "production mod3 module_key" repm3 = on(master, puppet('lookup', 'mod3::module_key')) result = repm3.stdout assert_match( /module-production-mod3-puppet-function/, result, "production mod3 module_key lookup failed, expected 'module-production-mod3-puppet-function'" ) step "production mod4 module_key" on(master, puppet('lookup', 'mod4::module_key'), :acceptable_exit_codes => [1]) step "env1 mod1 module_key" re1m1 = on(master, puppet('lookup', '--environment env1', 'mod1::module_key')) result = re1m1.stdout assert_match( /module-env1-mod1-hiera/, result, "env1 mod1 module_key lookup failed, expected 'module-env1-mod1-hiera'" ) step "env1 mod2 module_key" re1m2 = on(master, puppet('lookup', '--environment env1', 'mod2::module_key')) result = re1m2.stdout assert_match( /module-env1-mod2-ruby-function/, result, "env1 mod2 module_key lookup failed, expected 'module-env1-mod2-ruby-function'" ) step "env1 mod3 module_key" re1m3 = on(master, puppet('lookup', '--environment env1', 'mod3::module_key')) result = re1m3.stdout assert_match( /module-env1-mod3-puppet-function/, result, "env1 mod3 module_key lookup failed, expected 'module-env1-mod3-puppet-function'" ) step "env1 mod4 module_key" on(master, puppet('lookup', '--environment env1', 'mod4::module_key'), :acceptable_exit_codes => [1]) step "env2 mod1 module_key" re2m1 = on(master, puppet('lookup', '--environment env2', 'mod1::module_key')) result = re2m1.stdout assert_match( /module-env2-mod1-hiera/, result, "env2 mod1 module_key lookup failed, expected 'module-env2-mod1-hiera'" ) step "env2 mod2 module_key" re2m2 = on(master, puppet('lookup', '--environment env2', 'mod2::module_key')) result = re2m2.stdout assert_match( /module-env2-mod2-ruby-function/, result, "env2 mod2 module_key lookup failed, expected 'module-env2-mod2-ruby-function'" ) step "env2 mod3 module_key" re2m3 = on(master, puppet('lookup', '--environment env2', 'mod3::module_key')) result = re2m3.stdout assert_match( /module-env2-mod3-puppet-function/, result, "env2 mod3 module_key lookup failed, expected 'module-env2-mod3-puppet-function'" ) step "env2 mod4 module_key" on(master, puppet('lookup', '--environment env2', 'mod4::module_key'), :acceptable_exit_codes => [1]) step "env3 mod1 module_key" re3m1 = on(master, puppet('lookup', '--environment env3', 'mod1::module_key')) result = re3m1.stdout assert_match( /module-env3-mod1-hiera/, result, "env3 mod1 module_key lookup failed, expected 'module-env3-mod1-hiera'" ) step "env3 mod2 module_key" re3m2 = on(master, puppet('lookup', '--environment env3', 'mod2::module_key')) result = re3m2.stdout assert_match( /module-env3-mod2-ruby-function/, result, "env3 mod2 module_key lookup failed, expected 'module-env3-mod2-ruby-function'" ) step "env3 mod3 module_key" re3m3 = on(master, puppet('lookup', '--environment env3', 'mod3::module_key')) result = re3m3.stdout assert_match( /module-env3-mod3-puppet-function/, result, "env3 mod3 module_key lookup failed, expected 'module-env3-mod3-puppet-function'" ) step "env3 mod4 module_key" # re3m4 = on(master, puppet('lookup', '--environment env3', 'mod4::module_key'), :acceptable_exit_codes => [1]) step "env4 mod1 module_key" re4m1 = on(master, puppet('lookup', '--environment env4', 'mod1::module_key')) result = re4m1.stdout assert_match( /module-env4-mod1-hiera/, result, "env4 mod2 environent_key lookup failed, expected 'module-env4-hiera'" ) step "env4 mod2 module_key" re4m2 = on(master, puppet('lookup', '--environment env4', 'mod2::module_key')) result = re4m2.stdout assert_match( /module-env4-mod2-ruby-function/, result, "env4 mod2 environent_key lookup failed, expected 'module-env4-mod2-ruby-function'" ) step "env4 mod3 module_key" re4m3 = on(master, puppet('lookup', '--environment env4', 'mod3::module_key')) result = re4m3.stdout assert_match( /module-env4-mod3-puppet-function/, result, "env4 mod3 module_key lookup failed, expected 'module-env4-mod3-puppet-function'" ) step "env4 mod4 module_key" on(master, puppet('lookup', '--environment env4', 'mod4::module_key'), :acceptable_exit_codes => [1]) step "global key explained" rxg = on(master, puppet('lookup', '--explain', 'global_key')) result = rxg.stdout assert_match( /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*Found key.*global-hiera/, result, "global_key explained failed, expected /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*Found key.*global-hiera/" ) step "environment env1 environment_key explained" rxe1 = on(master, puppet('lookup', '--explain', '--environment env1', 'environment_key')) result = rxe1.stdout assert_match( /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key/, result, "environment env1 enviroment_key lookup failed, expected /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key/" ) assert_match( /common.*\s*.*env-env1 hiera/, result, "environment env1 enviroment_key lookup failed, expected /common.*\s*.*env-env1 hiera/" ) step "environment env2 environment_key explained" rxe2 = on(master, puppet('lookup', '--explain', '--environment env2', 'environment_key')) result = rxe2.stdout assert_match( /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key/, result, "environment env2 enviroment_key lookup failed, expected /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key/" ) assert_match( /eprecated API function.*\s*.*env-env2-ruby-function/, result, "environment env2 enviroment_key lookup failed, expected /eprecated API function.*\s*.*env-env2-ruby-function/" ) step "environment env3 environment_key explained" rxe3 = on(master, puppet('lookup', '--explain', '--environment env3', 'environment_key')) result = rxe3.stdout assert_match( /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key/, result, "environment env3 enviroment_key lookup failed, expected /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key/" ) assert_match( /eprecated API function.*\s*.*env-env3-puppet-function/, result, "environment env3 enviroment_key lookup failed, expected /eprecated API function.*\s*.*env-env3-puppet-function/" ) step "environment env4 environment_key explained" rxe4 = on(master, puppet('lookup', '--explain', '--environment env4', 'environment_key')) result = rxe4.stdout assert_match( /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key.*environment_key/, result, "environment env4 environment_key lookup failed expected /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key.*environment_key.*\s.*did not find a value.*/" ) step "environment env1 mod4::module_key explained" rxe1m4 = on(master, puppet('lookup', '--explain', '--environment env1', 'mod4::module_key')) result = rxe1m4.stdout assert_match( /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key.*\s*Env.*\s*.*env1\/hiera.yaml\"\s*Hier.*common\"\s*Path.*\s*Orig.*\s*No such key.*\s*Module data provider.*not found\s*.*did not find a value.*/, result, "environment env1 mod4::module_key lookup explained failed." ) step "environment env2 mod3::module_key explained" rxe2m3 = on(master, puppet('lookup', '--explain', '--environment env2', 'mod3::module_key')) result = rxe2m3.stdout assert_match( /Global Data Provider.*Using configuration.*Hierarchy entry.*Path.*No such key/m, result, "global env2 mod3::module_key lookup --explain had correct output" ) assert_match( /Environment Data Provider.*Deprecated.*No such key/m, result, "environment env2 mod3::module_key lookup --explain had correct output" ) assert_match( /Module.*Data Provider.*Deprecated API function "mod3::data".*Found key.*module-env2-mod3-puppet-function provided value/m, result, "module env2 mod3::module_key lookup --explain had correct output" ) step "environment env3 mod2::module_key explained" rxe3m2 = on(master, puppet('lookup', '--explain', '--environment env3', 'mod2::module_key')) result = rxe3m2.stdout assert_match( /Global Data Provider.*Using configuration.*Hierarchy entry.*Path.*No such key/m, result, "global env2 mod3::module_key lookup --explain had correct output" ) assert_match( /Environment Data Provider.*Deprecated.*No such key/m, result, "environment env2 mod3::module_key lookup --explain had correct output" ) assert_match( /Module.*Data Provider.*Deprecated API function "mod2::data".*Found key.*module-env3-mod2-ruby-function provided value/m, result, "module env2 mod3::module_key lookup --explain had correct output" ) step "environment env4 mod1::module_key explained" rxe4m1 = on(master, puppet('lookup', '--explain', '--environment env4', 'mod1::module_key')) result = rxe4m1.stdout assert_match( /Global Data Provider.*\s*Using.*\s*Hier.*\s*Path.*\s*Orig.*\s*No such key.*\s*Module.*Data Provider.*\s*Using.*\s*Hier.*common\"\s*Path.*\s*Orig.*\s*Found key.*module-env4-mod1-hiera/, result, "environment env4 mod1::module_key lookup failed." ) step 'apply env1 puppet.conf manifest' apply_manifest_on(master, @env1puppetconfmanifest, :catch_failures => true) step "puppet.conf specified environment env1 environment_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'environment_key')) result = r.stdout assert_match( /env-env1 hiera/, result, "puppet.conf specified environment env1, environment_key lookup failed, expected /env-env1 hiera/" ) step "puppet.conf specified environment env1 mod4::module_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'mod4::module_key'), :acceptable_exit_codes => [1]) step 'apply env2 puppet.conf manifest' apply_manifest_on(master, @env2puppetconfmanifest, :catch_failures => true) step "puppet.conf specified environment env2 environment_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'environment_key')) result = r.stdout assert_match( /env-env2-ruby-function/, result, "puppet.conf specified environment env2, environment_key lookup failed, expected /env-env2-ruby-function/" ) step "puppet.conf specified environment env2 mod3::module_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'mod3::module_key')) result = r.stdout assert_match( /module-env2-mod3-puppet-function/, result, "puppet.conf specified environment env2 mod3::module_key lookup failed, expeccted /module-env2-mod3-puppet-function/" ) step 'apply env3 puppet.conf manifest' apply_manifest_on(master, @env3puppetconfmanifest, :catch_failures => true) step "puppet.conf specified environment env3 environment_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'environment_key')) result = r.stdout assert_match( /env-env3-puppet-function/, result, "puppet.conf specified environment env1, environment_key lookup failed, expected /env-env3-puppet-function/" ) step "puppet.conf specified environment env3 mod2::module_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'mod2::module_key')) result = r.stdout assert_match( /module-env3-mod2-ruby-function/, result, "puppet.conf specified environment env2 mod3::module_key lookup failed, expeccted /module-env3-mod2-ruby-function/" ) step 'apply env4 puppet.conf manifest' apply_manifest_on(master, @env4puppetconfmanifest, :catch_failures => true) step "puppet.conf specified environment env4 environment_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'environment_key'), :acceptable_exit_codes => [1]) step "puppet.conf specified environment env4 mod1::module_key" r = on(master, puppet('lookup', "--confdir #{@confdir}", 'mod1::module_key')) result = r.stdout assert_match( /module-env4-mod1-hiera/, result, "puppet.conf specified environment env4 mod1::module_key lookup failed, expeccted /module-env4-mod1-hiera/" ) step 'apply enc manifest' apply_manifest_on(master, @encmanifest, :catch_failures => true) step "--compile uses environment specified in ENC" r = on(master, puppet('lookup', '--compile', "--node #{@node1}", "--confdir #{@confdir}", "--facts #{@coderoot}/facts.yaml", 'environment_key')) result = r.stderr assert_match( /CA is not available/, result, "lookup in ENC specified environment failed" ) step "handle certificate" on(master, "puppetserver ca generate --certname #{@node1}") on(master, "puppetserver ca generate --certname #{@node2}") on(master, "mkdir -p #{@testroot}/puppet/ssl/certs") on(master, "mkdir -p #{@testroot}/puppet/ssl/private_keys") on(master, "cp -a /etc/puppetlabs/puppet/ssl/certs/ca.pem #{@testroot}/puppet/ssl/certs") on(master, "cp -a /etc/puppetlabs/puppet/ssl/crl.pem #{@testroot}/puppet/ssl") on(master, "cp -a /etc/puppetlabs/puppet/ssl/private_keys/#{master.connection.hostname}.pem #{@testroot}/puppet/ssl/private_keys") on(master, "cp -a /etc/puppetlabs/puppet/ssl/certs/#{master.connection.hostname}.pem #{@testroot}/puppet/ssl/certs") step "--compile uses environment specified in ENC" r = on(master, puppet('lookup', '--compile', "--node #{@node1}", "--confdir #{@confdir}", "--facts #{@coderoot}/facts.yaml", 'environment_key')) result = r.stdout assert_match( /env-env2-ruby-function/, result, "lookup in ENC specified environment failed" ) step "without --compile does not use environment specified in ENC" r = on(master, puppet('lookup', "--node #{@node1}", "--confdir #{@confdir}", "--facts #{@coderoot}/facts.yaml", 'environment_key')) result = r.stdout assert_match( /env-production hiera provided value/, result, "lookup in production environment failed" ) step "lookup fails when there are no facts available" r = on(master, puppet('lookup', '--compile', "--node #{@node1}", "--confdir #{@confdir}", 'environment_key'), :acceptable_exit_codes => [1]) result = r.stderr assert_match( /No facts available/, result, "Expected to raise when there were no facts available." ) end puppetlabs-puppet-789f600/acceptance/tests/pluginsync/000077500000000000000000000000001470131746300231225ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/pluginsync/3935_pluginsync_should_follow_symlinks.rb000066400000000000000000000025361470131746300332240ustar00rootroot00000000000000test_name "pluginsync should not error when modulepath is a symlink and no modules have plugin directories" tag 'audit:high', 'audit:integration', 'server' step "Create a modulepath directory which is a symlink and includes a module without facts.d or lib directories" basedir = master.tmpdir("symlink_modulepath") target = "#{basedir}/target_dir" test_module_dir = "#{target}/module1" link_dest = "#{basedir}/link_dest" modulepath = "#{link_dest}" modulepath << "#{master['sitemoduledir']}" if master.is_pe? apply_manifest_on(master, < true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{basedir}':; '#{target}':; '#{test_module_dir}':; } file { '#{link_dest}': ensure => link, target => '#{target}', } MANIFEST master_opts = { 'main' => { 'basemodulepath' => "#{modulepath}" } } with_puppet_running_on master, master_opts, basedir do agents.each do |agent| on(agent, puppet('agent', "-t")) do |result| refute_match(/Could not retrieve information from environment production source\(s\) puppet:\/\/\/pluginfacts/, result.stderr) refute_match(/Could not retrieve information from environment production source\(s\) puppet:\/\/\/plugins/, result.stderr) end end end 4420_pluginfacts_should_be_resolvable_on_agent.rb000066400000000000000000000101451470131746300344750ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/pluginsynctest_name "Pluginsync'ed external facts should be resolvable on the agent" do tag 'audit:high', 'audit:integration' # # This test is intended to ensure that external facts downloaded onto an agent via # pluginsync are resolvable. In Linux, the external fact should have the same # permissions as its source on the master. # step "Create a codedir with a manifest and test module with external fact" codedir = master.tmpdir('4420-codedir') site_manifest_content = < true) File { ensure => directory, mode => "0755", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{codedir}':; '#{codedir}/environments':; '#{codedir}/environments/production':; '#{codedir}/environments/production/manifests':; '#{codedir}/environments/production/modules':; '#{codedir}/environments/production/modules/mymodule':; '#{codedir}/environments/production/modules/mymodule/manifests':; '#{codedir}/environments/production/modules/mymodule/facts.d':; } file { '#{codedir}/environments/production/manifests/site.pp': ensure => file, content => '#{site_manifest_content}', } file { '#{codedir}/environments/production/modules/mymodule/manifests/init.pp': ensure => file, content => 'class mymodule {}', } file { '#{codedir}/environments/production/modules/mymodule/facts.d/unix_external_fact.sh': ensure => file, mode => '755', content => '#{unix_fact}', } file { '#{codedir}/environments/production/modules/mymodule/facts.d/win_external_fact.bat': ensure => file, mode => '644', content => '#{win_fact}', } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{codedir}/environments" } } with_puppet_running_on(master, master_opts, codedir) do agents.each do |agent| factsd = agent.tmpdir('facts.d') pluginfactdest = agent.tmpdir('facts.d') teardown do on(master, "rm -rf '#{codedir}'") on(agent, "rm -rf '#{factsd}' '#{pluginfactdest}'") end step "Pluginsync the external fact to the agent and ensure it resolves correctly" do on(agent, puppet('agent', '-t', '--pluginfactdest', factsd), :acceptable_exit_codes => [2]) do |result| assert_match(/foo is bar/, result.stdout) end end step "Use plugin face to download to the agent" do on(agent, puppet('plugin', 'download', '--pluginfactdest', pluginfactdest)) do |result| assert_match(/Downloaded these plugins: .*external_fact/, result.stdout) unless agent['locale'] == 'ja' end end step "Ensure it resolves correctly" do on(agent, puppet('apply', '--pluginfactdest', pluginfactdest, '-e', "'notify { \"foo is ${foo}\": }'")) do |result| assert_match(/foo is bar/, result.stdout) end end # Linux specific tests next if agent['platform'] =~ /windows/ step "In Linux, ensure the pluginsync'ed external fact has the same permissions as its source" do on(agent, puppet('resource', "file '#{factsd}/unix_external_fact.sh'")) do |result| assert_match(/0755/, result.stdout) end end step "In Linux, ensure puppet apply uses the correct permissions" do test_source = File.join('/', 'tmp', 'test') on(agent, puppet('apply', "-e \"file { '#{test_source}': ensure => file, mode => '0456' }\"")) { 'source_permissions => use,' => /0456/, 'source_permissions => ignore,' => /0644/, '' => /0644/ }.each do |source_permissions, mode| on(agent, puppet('apply', "-e \"file { '/tmp/test_target': ensure => file, #{source_permissions} source => '#{test_source}' }\"")) on(agent, puppet('resource', "file /tmp/test_target")) do |result| assert_match(mode, result.stdout) end on(agent, "rm -f /tmp/test_target") end end end end end 4847_pluginfacts_should_be_resolvable_from_applications.rb000066400000000000000000000044761470131746300364430ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/pluginsynctest_name "Pluginsync'ed custom facts should be resolvable during application runs" do tag 'audit:high', 'audit:integration' # # This test is intended to ensure that custom facts downloaded onto an agent via # pluginsync are resolvable by puppet applications besides agent/apply. # require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils tmp_environment = mk_tmp_environment_with_teardown(master, 'resolve') master_module_dir = "#{environmentpath}/#{tmp_environment}/modules/module_name" master_type_dir = "#{master_module_dir}/lib/puppet/type" master_module_type_file = "#{master_type_dir}/test4847.rb" master_provider_dir = "#{master_module_dir}/lib/puppet/provider/test4847" master_provider_file = "#{master_provider_dir}/only.rb" master_facter_dir = "#{master_module_dir}/lib/facter" master_facter_file = "#{master_facter_dir}/foo.rb" on(master, "mkdir -p '#{master_type_dir}' '#{master_provider_dir}' '#{master_facter_dir}'") teardown do on(master, "rm -rf '#{master_module_dir}'") # Remove all traces of the last used environment agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end test_type = <<-TYPE Puppet::Type.newtype(:test4847) do newparam(:name, :namevar => true) end TYPE create_remote_file(master, master_module_type_file, test_type) test_provider = <<-PROVIDER Puppet::Type.type(:test4847).provide(:only) do def self.instances warn "fact foo=\#{Facter.value('foo')}" [] end end PROVIDER create_remote_file(master, master_provider_file, test_provider) foo_fact_content = <<-FACT_FOO Facter.add('foo') do setcode do 'bar' end end FACT_FOO create_remote_file(master, master_facter_file, foo_fact_content) on(master, "chmod 755 '#{master_module_type_file}' '#{master_provider_file}' '#{master_facter_file}'") with_puppet_running_on(master, {}) do agents.each do |agent| on(agent, puppet("agent -t --environment #{tmp_environment}")) on(agent, puppet('resource test4847')) do |result| assert_match(/fact foo=bar/, result.stderr) end end end end 7316_apps_should_be_available_via_pluginsync.rb000066400000000000000000000072601470131746300341560ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/pluginsynctest_name 'the pluginsync functionality should sync app definitions, and they should be runnable afterwards' do tag 'audit:high', 'audit:integration' # # This test is intended to ensure that pluginsync syncs app definitions to the agents. # Further, the apps should be runnable on the agent after the sync has occurred. # require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils tmp_environment = mk_tmp_environment_with_teardown(master, 'app') master_module_dir = "#{environmentpath}/#{tmp_environment}/modules" on(master, "mkdir -p '#{master_module_dir}'") teardown do on(master, "rm -rf '#{master_module_dir}'") # Remove all traces of the last used environment agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end app_name = "superbogus" app_desc = "a simple application for testing application delivery via plugin sync" app_output = "Hello from the #{app_name} application" master_module_file_content = <<-HERE require 'puppet/application' class Puppet::Application::#{app_name.capitalize} < Puppet::Application def help <<-HELP puppet-#{app_name}(8) -- #{app_desc} ======== HELP end def main() puts("#{app_output}") end end HERE # here we create a custom app, which basically doesn't do anything except # for print a hello-world message # master_module_app_path = "#{master_module_dir}/#{app_name}/lib/puppet/application" master_module_app_file = "#{master_module_app_path}/#{app_name}.rb" on(master, "mkdir -p '#{master_module_app_path}'") create_remote_file(master, master_module_app_file, master_module_file_content) on(master, "chmod 755 '#{master_module_app_file}'") step "start the master" do with_puppet_running_on(master, {}) do agents.each do |agent| agent_lib_dir = agent.tmpdir('agent_lib_sync') agent_module_app_file = "#{agent_lib_dir}/puppet/application/#{app_name}.rb" teardown do on(agent, "rm -rf '#{agent_lib_dir}'") end # the module files shouldn't exist on the agent yet because they haven't been synced step "verify that the module files don't exist on the agent path" do if file_exists?(agent, agent_module_app_file) fail_test("app file already exists on agent: '#{agent_module_app_file}'") end end step "run the agent" do on(agent, puppet("agent --libdir='#{agent_lib_dir}' --test --environment '#{tmp_environment}'")) do |result| refute_match( /The \`source_permissions\` parameter is deprecated/, result.stderr, "pluginsync should not get a deprecation warning for source_permissions") end end step "verify that the module files were synced down to the agent" do unless file_exists?(agent, agent_module_app_file) fail_test("The app file we expect was not not synced to agent: '#{agent_module_app_file}'") end end step "verify that the application shows up in help" do on(agent, PuppetCommand.new(:help, "--libdir='#{agent_lib_dir}'")) do |result| assert_match(/^\s+#{app_name}\s+#{app_desc}/, result.stdout) end end step "verify that we can run the application" do on(agent, PuppetCommand.new(:"#{app_name}", "--libdir='#{agent_lib_dir}'")) do |result| assert_match(/^#{app_output}/, result.stdout) end end end end end end 7316_faces_with_app_stubs_should_be_available_via_pluginsync.rb000066400000000000000000000137541470131746300374140ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/pluginsynctest_name "the pluginsync functionality should sync app definitions, and they should be runnable afterwards" tag 'audit:high', 'audit:integration', 'server' # # This test is intended to ensure that pluginsync syncs face definitions to the agents. # Further, the face should be runnable on the agent after the sync has occurred. # # (NOTE: When this test is passing, it should resolve both #7316 re: verifying that apps/faces can # be run on the agent node after a plugin sync, and #6753 re: being able to run a face without # having a placeholder stub file in the "applications" directory.) # require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils initialize_temp_dirs() all_tests_passed = false ############################################################################### # BEGIN TEST LOGIC ############################################################################### # create some vars to point to the directories that we're going to point the master/agents at environments_dir = "environments" master_module_dir = "#{environments_dir}/production/modules" agent_lib_dir = "agent_lib" app_name = "superbogus" app_desc = "a simple %1$s for testing %1$s delivery via plugin sync" app_output = "Hello from the #{app_name} %s" master_module_face_content = <<-HERE Puppet::Face.define(:#{app_name}, '0.0.1') do copyright "Puppet Labs", 2011 license "Apache 2 license; see COPYING" summary "#{app_desc % "face"}" action(:foo) do summary "a test action defined in the test face in the main puppet lib dir" default when_invoked do |*args| puts "#{app_output % "face"}" end end end HERE master_module_app_content = <<-HERE require 'puppet/application/face_base' class Puppet::Application::#{app_name.capitalize} < Puppet::Application::FaceBase end HERE # this begin block is here for handling temp file cleanup via an "ensure" block # at the very end of the test. begin # here we create a custom app, which basically doesn't do anything except for # print a hello-world message agent_module_face_file = "#{agent_lib_dir}/puppet/face/#{app_name}.rb" master_module_face_file = "#{master_module_dir}/#{app_name}/lib/puppet/face/#{app_name}.rb" agent_module_app_file = "#{agent_lib_dir}/puppet/application/#{app_name}.rb" master_module_app_file = "#{master_module_dir}/#{app_name}/lib/puppet/application/#{app_name}.rb" # copy all the files to the master step "write our simple module out to the master" do create_test_file(master, master_module_app_file, master_module_app_content, :mkdirs => true) create_test_file(master, master_module_face_file, master_module_face_content, :mkdirs => true) end step "verify that the app file exists on the master" do unless test_file_exists?(master, master_module_app_file) then fail_test("Failed to create app file '#{get_test_file_path(master, master_module_app_file)}' on master") end unless test_file_exists?(master, master_module_face_file) then fail_test("Failed to create face file '#{get_test_file_path(master, master_module_face_file)}' on master") end end master_opts = { 'main' => { 'environmentpath' => "#{get_test_file_path(master, environments_dir)}", } } step "start the master" do with_puppet_running_on master, master_opts do # the module files shouldn't exist on the agent yet because they haven't been synced step "verify that the module files don't exist on the agent path" do agents.each do |agent| if test_file_exists?(agent, agent_module_app_file) then fail_test("app file already exists on agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end if test_file_exists?(agent, agent_module_app_file) then fail_test("face file already exists on agent: '#{get_test_file_path(agent, agent_module_face_file)}'") end end end step "run the agent" do agents.each do |agent| on(agent, puppet('agent', "--libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\" ", "--trace --test") ) end end end end step "verify that the module files were synced down to the agent" do agents.each do |agent| unless test_file_exists?(agent, agent_module_app_file) then fail_test("The app file we expected was not synced to the agent: '#{get_test_file_path(agent, agent_module_app_file)}'") end unless test_file_exists?(agent, agent_module_face_file) then fail_test("The face file we expected was not not synced to the agent: '#{get_test_file_path(agent, agent_module_face_file)}'") end end end step "verify that the application shows up in help" do agents.each do |agent| on(agent, PuppetCommand.new(:help, "--libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\"")) do |result| assert_match(/^\s+#{app_name}\s+#{app_desc % "face"}/, result.stdout) end end end step "verify that we can run the application" do agents.each do |agent| on(agent, PuppetCommand.new(:"#{app_name}", "--libdir=\"#{get_test_file_path(agent, agent_lib_dir)}\"")) do |result| assert_match(/^#{app_output % "face"}/, result.stdout) end end end step "clear out the libdir on the agents in preparation for the next test" do agents.each do |agent| on(agent, "rm -rf #{get_test_file_path(agent, agent_lib_dir)}/*") end end all_tests_passed = true ensure ########################################################################################## # Clean up all of the temp files created by this test. It would be nice if this logic # could be handled outside of the test itself; I envision a stanza like this one appearing # in a very large number of the tests going forward unless it is handled by the framework. ########################################################################################## if all_tests_passed then remove_temp_dirs() end end puppetlabs-puppet-789f600/acceptance/tests/pluginsync/feature/000077500000000000000000000000001470131746300245555ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/pluginsync/feature/pluginsync_should_sync_features.rb000066400000000000000000000171541470131746300336150ustar00rootroot00000000000000test_name "the pluginsync functionality should sync feature and function definitions" do tag 'audit:high', 'audit:integration' # # This test is intended to ensure that pluginsync syncs feature definitions to # the agents. It checks the feature twice; once to make sure that it gets # loaded successfully during the run in which it was synced, and once to ensure # that it still gets loaded successfully during the subsequent run (in which it # should not be synced because the files haven't changed.) # require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils teardown do agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end module_name = 'superbogus' tmp_environment = mk_tmp_environment_with_teardown(master, 'sync') master_module_dir = "#{environmentpath}/#{tmp_environment}/modules" master_module_type_path = "#{master_module_dir}/#{module_name}/lib/puppet/type/" master_module_feature_path = "#{master_module_dir}/#{module_name}/lib/puppet/feature" master_module_function_path = "#{master_module_dir}/#{module_name}/lib/puppet/functions" master_module_function_path_namespaced = "#{master_module_dir}/#{module_name}/lib/puppet/functions/superbogus" on(master, "mkdir -p '#{master_module_dir}'") on(master, "mkdir -p '#{master_module_type_path}' '#{master_module_feature_path}' '#{master_module_function_path}' #{master_module_function_path_namespaced}") master_module_type_file = "#{master_module_type_path}/#{module_name}.rb" master_module_type_content = <<-HERE module Puppet Type.newtype(:#{module_name}) do newparam(:name) do isnamevar end newproperty(:testfeature) do def sync Puppet.info("The value of the #{module_name} feature is: \#{Puppet.features.#{module_name}?}") end def retrieve :absent end def insync?(is) false end end end end HERE create_remote_file(master, master_module_type_file, master_module_type_content) master_module_feature_file = "#{master_module_feature_path}/#{module_name}.rb" master_module_feature_content = <<-HERE Puppet.features.add(:#{module_name}) do Puppet.info("#{module_name} feature being queried") true end HERE create_remote_file(master, master_module_feature_file, master_module_feature_content) on(master, "chmod 755 '#{master_module_type_file}' '#{master_module_feature_file}'") master_module_function_file = "#{master_module_function_path}/bogus_function.rb" master_module_function_content = <<-HERE Puppet::Functions.create_function(:bogus_function) do dispatch :bogus_function do end def bogus_function() three = call_function('round', 3.14) hostname = `facter hostname` "Three is \#{three}. bogus_function reporting hostname is \#{hostname}" end end HERE create_remote_file(master, master_module_function_file, master_module_function_content) on(master, "chmod 755 '#{master_module_function_file}' '#{master_module_function_file}'") master_module_namespaced_function_file = "#{master_module_function_path_namespaced}/bogus_function2.rb" master_module_namespaced_function_content = <<-HERE Puppet::Functions.create_function(:'superbogus::bogus_function2') do dispatch :bogus_function2 do end def bogus_function2() four = call_function('round', 4.14) hostname = `facter hostname` "Four is \#{four}. bogus_function reporting hostname is \#{hostname}" end end HERE create_remote_file(master, master_module_namespaced_function_file, master_module_namespaced_function_content) on(master, "chmod 755 '#{master_module_namespaced_function_file}'") site_pp = <<-HERE #{module_name} { "This is the title of the #{module_name} type instance in site.pp": testfeature => "Hi. I'm setting the testfeature property of #{module_name} here in site.pp", } notify { module_function: message => Deferred('bogus_function', []) } notify { module_function2: message => Deferred('superbogus::bogus_function2', []) } HERE create_sitepp(master, tmp_environment, site_pp) # These master opts should not be necessary whence content negotation for # Puppet 6.0.0 is completed, and this should just be removed. master_opts = { 'master' => { 'rich_data' => 'true' } } step 'start the master' do with_puppet_running_on(master, master_opts) do agents.each do |agent| agent_lib_dir = agent.tmpdir('libdir') agent_module_type_file = "#{agent_lib_dir}/puppet/type/#{module_name}.rb" agent_module_feature_file = "#{agent_lib_dir}/puppet/feature/#{module_name}.rb" agent_module_function_file = "#{agent_lib_dir}/puppet/functions/bogus_function.rb" agent_module_namespaced_function_file = "#{agent_lib_dir}/puppet/functions/superbogus/bogus_function2.rb" facter_hostname = fact_on(agent, 'hostname') step "verify that the module files don't exist on the agent path" do [agent_module_type_file, agent_module_feature_file, agent_module_function_file].each do |file_path| if file_exists?(agent, file_path) fail_test("file should not exist on the agent yet: '#{file_path}'") end end end step 'run the agent and verify that it loaded the feature' do on(agent, puppet("agent -t --libdir='#{agent_lib_dir}' --rich_data --environment '#{tmp_environment}'"), :acceptable_exit_codes => [2]) do |result| assert_match(/The value of the #{module_name} feature is: true/, result.stdout, "Expected agent stdout to include confirmation that the feature was 'true'") assert_match(/Three is 3. bogus_function reporting hostname is #{facter_hostname}/, result.stdout, "Expect the agent stdout to run bogus_function and report hostname") assert_match(/Four is 4. bogus_function reporting hostname is #{facter_hostname}/, result.stdout, "Expect the agent stdout to run bogus_function and report hostname") end end step 'verify that the module files were synced down to the agent' do [agent_module_type_file, agent_module_feature_file, agent_module_function_file, agent_module_namespaced_function_file].each do |file_path| unless file_exists?(agent, file_path) fail_test("Expected file to exist on the agent now: '#{file_path}'") end end end step 'run the agent again with a cached catalog' do on(agent, puppet("agent -t --libdir='#{agent_lib_dir}' --use_cached_catalog --rich_data --environment '#{tmp_environment}'"), :acceptable_exit_codes => [2]) do |result| assert_match(/The value of the #{module_name} feature is: true/, result.stdout, "Expected agent stdout to include confirmation that the feature was 'true'") assert_match(/Three is 3. bogus_function reporting hostname is #{facter_hostname}/, result.stdout, "Expect the agent stdout to run bogus_function and report hostname") assert_match(/Four is 4. bogus_function reporting hostname is #{facter_hostname}/, result.stdout, "Expect the agent stdout to run bogus_function and report hostname") end end end end end end files_earlier_in_modulepath_take_precendence.rb000066400000000000000000000032571470131746300344530ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/pluginsynctest_name "earlier modules take precendence over later modules in the modulepath" tag 'audit:high', 'audit:integration', 'server' step "Create some modules in the modulepath" basedir = master.tmpdir("module_precedence") module_dir1 = "#{basedir}/environments/production/modules1" module_dir2 = "#{basedir}/modules2" modulepath = "#{module_dir1}:#{module_dir2}" modulepath << ":#{master['sitemoduledir']}" if master.is_pe? apply_manifest_on(master, < true) File { ensure => directory, mode => "0750", owner => #{master.puppet['user']}, group => #{master.puppet['group']}, } file { '#{basedir}':; '#{module_dir2}':; '#{module_dir2}/a':; '#{module_dir2}/a/lib':; '#{basedir}/environments':; '#{basedir}/environments/production':; '#{module_dir1}':; '#{module_dir1}/a':; '#{module_dir1}/a/lib':; } file { '#{basedir}/environments/production/environment.conf': ensure => file, content => "modulepath='#{modulepath}'", mode => "0640", } file { "mod1": ensure => file, path => "#{module_dir1}/a/lib/foo.rb", content => "'from the first module'", mode => "0640", } file { "mod2": ensure => file, path => "#{module_dir2}/a/lib/foo.rb", content => "'from the second module'", mode => "0640", } MANIFEST master_opts = { 'main' => { 'environmentpath' => "#{basedir}/environments", } } with_puppet_running_on master, master_opts, basedir do agents.each do |agent| on(agent, puppet('agent', "-t")) on(agent, "cat \"#{agent.puppet['vardir']}/lib/foo.rb\"") do |result| assert_match(/from the first module/, result.stdout, "The synced plugin was not found or the wrong version was synced") end end end puppetlabs-puppet-789f600/acceptance/tests/provider/000077500000000000000000000000001470131746300225615ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/provider/package/000077500000000000000000000000001470131746300241545ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/provider/package/apt_install_package_with_range.rb000066400000000000000000000035511470131746300327010ustar00rootroot00000000000000test_name "apt can install range if package is not installed" do confine :to, :platform => /debian|ubuntu/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = "helloworld" available_package_versions = ['1.0-1', '1.19-1', '2.0-1'] repo_fixture_path = File.join(File.dirname(__FILE__), '..', '..', '..', 'fixtures', 'debian-repo') agents.each do |agent| scp_to(agent, repo_fixture_path, '/tmp') file_manifest = resource_manifest('file', '/etc/apt/sources.list.d/tmp.list', ensure: 'present', content: 'deb [trusted=yes] file:/tmp/debian-repo ./') apply_manifest_on(agent, file_manifest) on(agent, 'apt-get update') teardown do package_absent(agent, package, '--force-yes') file_manifest = resource_manifest('file', '/etc/apt/sources.list.d/tmp.list', ensure: 'absent') apply_manifest_on(agent, file_manifest) on(agent, 'rm -rf /tmp/debian-repo') on(agent, 'apt-get update') end step "Ensure that package is installed first if not present" do package_manifest = resource_manifest('package', package, ensure: "<=#{available_package_versions[1]}") apply_manifest_on(agent, package_manifest) installed_package_version = on(agent.name, "apt-cache policy #{package} | sed -n -e 's/Installed: //p'").stdout assert_match(available_package_versions[1], installed_package_version) end step "Ensure that package is updated" do package_manifest = resource_manifest('package', package, ensure: ">#{available_package_versions[1]}") apply_manifest_on(agent, package_manifest) installed_package_version = on(agent.name, "apt-cache policy #{package} | sed -n -e 's/Installed: //p'").stdout assert_match(available_package_versions[2], installed_package_version) end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/dnfmodule_enable_only.rb000066400000000000000000000037211470131746300310300ustar00rootroot00000000000000test_name "dnfmodule can change flavors" do confine :to, :platform => /el-8-x86_64/ # only el/centos 8 have the appstream repo tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils without_profile = '389-ds' with_profile = 'swig' agents.each do |agent| skip_test('appstream repo not present') unless on(agent, 'dnf repolist').stdout.include?('appstream') teardown do apply_manifest_on(agent, resource_manifest('package', without_profile, ensure: 'absent', provider: 'dnfmodule')) apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'absent', provider: 'dnfmodule')) end end step "Enable module with no default profile: #{without_profile}" do apply_manifest_on(agent, resource_manifest('package', without_profile, ensure: 'present', provider: 'dnfmodule'), expect_changes: true) on(agent, "dnf module list --enabled | grep #{without_profile}") end step "Ensure idempotency for: #{without_profile}" do apply_manifest_on(agent, resource_manifest('package', without_profile, ensure: 'present', provider: 'dnfmodule'), catch_changes: true) end step "Enable module with a profile: #{with_profile}" do apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'present', enable_only: true, provider: 'dnfmodule'), expect_changes: true) on(agent, "dnf module list --enabled | grep #{with_profile}") end step "Ensure idempotency for: #{with_profile}" do apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'present', enable_only: true, provider: 'dnfmodule'), catch_changes: true) end step "Install a flavor for: #{with_profile}" do apply_manifest_on(agent, resource_manifest('package', with_profile, ensure: 'present', flavor: 'common', provider: 'dnfmodule'), expect_changes: true) on(agent, "dnf module list --installed | grep #{with_profile}") end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/dnfmodule_ensure_versionable.rb000066400000000000000000000030371470131746300324330ustar00rootroot00000000000000test_name "dnfmodule is versionable" do confine :to, :platform => /el-8-x86_64/ # only el/centos 8 have the appstream repo tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = "postgresql" agents.each do |agent| skip_test('appstream repo not present') unless on(agent, 'dnf repolist').stdout.include?('appstream') teardown do apply_manifest_on(agent, resource_manifest('package', package, ensure: 'absent', provider: 'dnfmodule')) end end step "Ensure we get the newer version by default" do apply_manifest_on(agent, resource_manifest('package', package, ensure: 'present', provider: 'dnfmodule')) on(agent, 'postgres --version') do |version| assert_match('postgres (PostgreSQL) 10', version.stdout, 'package version not correct') end end step "Ensure we get a specific version if we want it" do apply_manifest_on(agent, resource_manifest('package', package, ensure: '9.6', provider: 'dnfmodule')) on(agent, 'postgres --version') do |version| assert_match('postgres (PostgreSQL) 9.6', version.stdout, 'package version not correct') end end step "Ensure we can disable a package" do apply_manifest_on(agent, resource_manifest('package', package, ensure: :disabled, provider: 'dnfmodule')) on(agent, "dnf module list | grep #{package}") do |output| output.stdout.each_line do |line| assert_match("\[x\]", line, 'package not disabled') end end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/dnfmodule_manages_flavors.rb000066400000000000000000000024551470131746300317130ustar00rootroot00000000000000test_name "dnfmodule can change flavors" do confine :to, :platform => /el-8-x86_64/ # only el/centos 8 have the appstream repo tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = "postgresql" agents.each do |agent| skip_test('appstream repo not present') unless on(agent, 'dnf repolist').stdout.include?('appstream') teardown do apply_manifest_on(agent, resource_manifest('package', package, ensure: 'absent', provider: 'dnfmodule')) end end step "Install the client #{package} flavor" do apply_manifest_on(agent, resource_manifest('package', package, ensure: 'present', flavor: 'client', provider: 'dnfmodule')) on(agent, "dnf module list --installed | grep #{package} | sed -E 's/\\[d\\] //g'") do |output| assert_match('client [i]', output.stdout, 'installed flavor not correct') end end step "Install the server #{package} flavor" do apply_manifest_on(agent, resource_manifest('package', package, ensure: 'present', flavor: 'server', provider: 'dnfmodule')) on(agent, "dnf module list --installed | grep #{package} | sed -E 's/\\[d\\] //g'") do |output| assert_match('server [i]', output.stdout, 'installed flavor not correct') end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/dpkg_ensure_latest_virtual_packages.rb000066400000000000000000000040731470131746300337730ustar00rootroot00000000000000test_name "dpkg ensure latest with allow_virtual set to true, the virtual package should detect and install a real package" do confine :to, :platform => /debian/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils pkg = "rubygems" agents.each do |agent| ruby_present = on(agent, 'dpkg -s ruby', accept_all_exit_codes: true).exit_code == 0 teardown do if ruby_present apply_manifest_on(agent, resource_manifest('package', 'ruby', ensure: 'present')) else apply_manifest_on(agent, resource_manifest('package', 'ruby', ensure: 'absent')) end end step "Uninstall system ruby if already present" do apply_manifest_on(agent, resource_manifest('package', 'ruby', ensure: 'absent')) if ruby_present end step "Ensure latest should install ruby instead of rubygems when allow_virtual is set to true" do package_manifest_with_allow_virtual = resource_manifest('package', pkg, ensure: 'latest', allow_virtual: true) apply_manifest_on(agent, package_manifest_with_allow_virtual, expect_changes: true) output = on(agent, "dpkg-query -W --showformat='${Status} ${Package} ${Version} [${Provides}]\n' ").output lines = output.split("\n") matched_line = lines.find { |package| package.match(/[\[ ](#{Regexp.escape(pkg)})[\],]/)} package_line_info = matched_line.split real_package_name = package_line_info[3] real_package_installed_version = package_line_info[4] installed_version = on(agent, "apt-cache policy #{real_package_name} | sed -n -e 's/Installed: //p'").stdout.strip assert_match(real_package_installed_version, installed_version) end step "Ensure latest should not install ruby package if it's already installed and exit code should be 0" do package_manifest_with_allow_virtual = resource_manifest('package', pkg, ensure: 'latest', allow_virtual: true) apply_manifest_on(agent, package_manifest_with_allow_virtual, :catch_changes => true) end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/dpkg_hold_true_package_is_latest.rb000066400000000000000000000016131470131746300332160ustar00rootroot00000000000000test_name "dpkg ensure hold package is latest installed" do confine :to, :platform => /debian-9-amd64/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = "nginx" agents.each do |agent| teardown do package_absent(agent, package, '--force-yes') end end step"Ensure that package is installed first if not present" do expected_package_version = on(agent.name, "apt-cache policy #{package} | sed -n -e 's/Candidate: //p'").stdout package_manifest = resource_manifest('package', package, mark: "hold") apply_manifest_on(agent, package_manifest) do |result| installed_package_version = on(agent.name, "apt-cache policy #{package} | sed -n -e 's/Installed: //p'").stdout assert_match(expected_package_version, installed_package_version) end end end dpkg_hold_true_should_preserve_version.rb000066400000000000000000000016001470131746300344470ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/provider/packagetest_name "dpkg ensure hold package should preserve version if package is already installed" do confine :to, :platform => /debian-9-amd64/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = "openssl" step "Ensure hold should lock to specific installed version" do existing_installed_version = on(agent.name, "dpkg -s #{package} | sed -n -e 's/Version: //p'").stdout existing_installed_version.delete!(' ') package_manifest_hold = resource_manifest('package', package, mark: "hold") apply_manifest_on(agent, package_manifest_hold) do installed_version = on(agent.name, "apt-cache policy #{package} | sed -n -e 's/Installed: //p'").stdout installed_version.delete!(' ') assert_match(existing_installed_version, installed_version) end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/gem.rb000066400000000000000000000122761470131746300252610ustar00rootroot00000000000000test_name "gem provider should install and uninstall" do confine :to, :template => /centos-7-x86_64|redhat-7-x86_64/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = 'colorize' agents.each do |agent| # On a Linux host with only the 'agent' role, the puppet command fails when another Ruby is installed earlier in the PATH: # # [root@agent ~]# env PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/opt/puppetlabs/bin" puppet apply -e ' notify { "Hello": }' # Activating bundler (2.0.2) failed: # Could not find 'bundler' (= 2.0.2) among 5 total gem(s) # To install the version of bundler this project requires, run `gem install bundler -v '2.0.2'` # # Magically, the puppet command succeeds on a Linux host with both the 'master' and 'agent' roles. # # Puppet's Ruby makes a fine target. Unfortunately, it's first in the PATH on Windows: PUP-6134. # Also, privatebindir isn't a directory on Windows, it's a PATH: # https://github.com/puppetlabs/beaker-puppet/blob/master/lib/beaker-puppet/install_utils/aio_defaults.rb # # These tests depend upon testing being confined to /centos-7-x86_64|redhat-7-x86_64/. if agent['roles'].include?('master') original_path = agent.get_env_var('PATH').split('=').last # https://github.com/puppetlabs/puppet-agent/blob/master/resources/files/puppet-agent.sh puppet_agent_sh_path = '/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/opt/puppetlabs/bin' system_gem_command = '/usr/bin/gem' teardown do step "Teardown: Uninstall System Ruby, and reset PATH" do package_absent(agent, 'ruby') agent.clear_env_var('PATH') agent.add_env_var('PATH', original_path) end end step "Setup: Install System Ruby, and set PATH to place System Ruby ahead of Puppet Ruby" do package_present(agent, 'ruby') agent.add_env_var('PATH', puppet_agent_sh_path) end step "Install a gem package in System Ruby" do package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'gem' } ) apply_manifest_on(agent, package_manifest, :catch_failures => true) do list = on(agent, "#{system_gem_command} list").stdout assert_match(/#{package} \(/, list) end on(agent, "#{system_gem_command} uninstall #{package}") end step "Uninstall a gem package in System Ruby" do on(agent, "/usr/bin/gem install #{package}") package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'gem' } ) apply_manifest_on(agent, package_manifest, :catch_failures => true) do list = on(agent, "#{system_gem_command} list").stdout refute_match(/#{package} \(/, list) end on(agent, "#{system_gem_command} uninstall #{package}") end step "Uninstall System Ruby, and reset PATH" do package_absent(agent, 'ruby') agent.add_env_var('PATH', original_path) end end puppet_gem_command = "#{agent['privatebindir']}/gem" step "Install a gem package with a target command" do package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'gem', command: puppet_gem_command } ) apply_manifest_on(agent, package_manifest, :catch_failures => true) do list = on(agent, "#{puppet_gem_command} list").stdout assert_match(/#{package} \(/, list) end on(agent, "#{puppet_gem_command} uninstall #{package}") end step "Install a gem package in a certain min max range" do package_manifest1 = resource_manifest('package', package, { ensure: '>0.5 <0.7', provider: 'gem' } ) package_manifest2 = resource_manifest('package', package, { ensure: '>0.7 <0.8.1', provider: 'gem' } ) # Install package (with version between 0.5 and 0.7) apply_manifest_on(agent, package_manifest1, :expect_changes => true) do list = on(agent, "#{puppet_gem_command} list").stdout assert_match(/#{package} \((0.6.0)\)/, list) end # Reapply same manifest and expect no changes apply_manifest_on(agent, package_manifest1, :catch_changes => true) # Install besides existing package (with version between 0.7 and 0.8.1) and expect changes apply_manifest_on(agent, package_manifest2, :expect_changes => true) do list = on(agent, "#{puppet_gem_command} list").stdout assert_match(/#{package} \((0.8.0, 0.6.0)\)/, list) end on(agent, "#{puppet_gem_command} uninstall #{package} --all") end step "Uninstall a gem package with a target command" do on(agent, "#{puppet_gem_command} install #{package}") package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'gem', command: puppet_gem_command } ) apply_manifest_on(agent, package_manifest, :catch_failures => true) do list = on(agent, "#{puppet_gem_command} list").stdout refute_match(/#{package} \(/, list) end on(agent, "#{puppet_gem_command} uninstall #{package}") end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/pip.rb000066400000000000000000000071411470131746300252740ustar00rootroot00000000000000test_name "pip provider should install, use install_options with latest, and uninstall" do confine :to, :template => /centos/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = 'colorize' pip_command = 'pip' teardown do on(agent, "#{pip_command} uninstall #{package} --disable-pip-version-check --yes", :accept_all_exit_codes => true) end agents.each do |agent| step "Setup: Install EPEL Repository, Python and Pip" do package_present(agent, 'epel-release') if agent.platform =~ /el-8/ package_present(agent, 'python2') package_present(agent, 'python2-pip') pip_command = 'pip2' else package_present(agent, 'python') package_present(agent, 'python-pip') end end step "Ensure presence of a pip package" do package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'pip' } ) apply_manifest_on(agent, package_manifest, :catch_failures => true) do list = on(agent, "#{pip_command} list --disable-pip-version-check").stdout assert_match(/#{package} \(/, list) end on(agent, "#{pip_command} uninstall #{package} --disable-pip-version-check --yes") end step "Install a pip package using version range" do package_manifest1 = resource_manifest('package', package, { ensure: '<=1.1.0', provider: 'pip' } ) package_manifest2 = resource_manifest('package', package, { ensure: '<1.0.4', provider: 'pip' } ) # Make a fresh package install (with version lower than or equal to 1.1.0) apply_manifest_on(agent, package_manifest1, :expect_changes => true) do list = on(agent, "#{pip_command} list --disable-pip-version-check").stdout match = list.match(/#{package} \((.+)\)/) installed_version = match[1] if match assert_match(installed_version, '1.1.0') end # Reapply same manifest and expect no changes apply_manifest_on(agent, package_manifest1, :catch_changes => true) # Reinstall over existing package (with version lower than 1.0.4) and expect changes (to be 1.0.3) apply_manifest_on(agent, package_manifest2, :expect_changes => true) do list = on(agent, "#{pip_command} list --disable-pip-version-check").stdout match = list.match(/#{package} \((.+)\)/) installed_version = match[1] if match assert_match(installed_version, '1.0.3') end on(agent, "#{pip_command} uninstall #{package} --disable-pip-version-check --yes") end step "Ensure latest with pip uses install_options" do on(agent, "#{pip_command} install #{package} --disable-pip-version-check") package_manifest = resource_manifest('package', package, { ensure: 'latest', provider: 'pip', install_options: { '--index' => 'https://pypi.python.org/simple' } } ) result = apply_manifest_on(agent, package_manifest, { :catch_failures => true, :debug => true } ) assert_match(/--index=https:\/\/pypi.python.org\/simple/, result.stdout) on(agent, "#{pip_command} uninstall #{package} --disable-pip-version-check --yes") end step "Uninstall a pip package" do on(agent, "#{pip_command} install #{package} --disable-pip-version-check") package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'pip' } ) apply_manifest_on(agent, package_manifest, :catch_failures => true) do list = on(agent, "#{pip_command} list --disable-pip-version-check").stdout refute_match(/#{package} \(/, list) end end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/puppetserver_gem.rb000066400000000000000000000025241470131746300301000ustar00rootroot00000000000000test_name "puppetserver_gem provider should install and uninstall" do tag 'audit:high', 'server' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils skip_test 'puppetserver_gem is only suitable on server nodes' unless master package = 'world_airports' teardown do # Ensure the gem is uninstalled if anything goes wrong # TODO maybe execute this only if something fails, as it takes time on(master, "puppetserver gem uninstall #{package}") end step "Installing a gem executes without error" do package_manifest = resource_manifest('package', package, { ensure: 'present', provider: 'puppetserver_gem' } ) apply_manifest_on(master, package_manifest, catch_failures: true) do list = on(master, "puppetserver gem list").stdout assert_match(/#{package} \(/, list) end # Run again for idempotency apply_manifest_on(master, package_manifest, catch_changes: true) end step "Uninstalling a gem executes without error" do package_manifest = resource_manifest('package', package, { ensure: 'absent', provider: 'puppetserver_gem' } ) apply_manifest_on(master, package_manifest, catch_failures: true) do list = on(master, "puppetserver gem list").stdout refute_match(/#{package} \(/, list) end end end rpm_ensure_install_multiversion_package.rb000066400000000000000000000075401470131746300346300ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/provider/packagetest_name "rpm should install packages with multiple versions" do confine :to, :platform => /redhat|centos|el|fedora/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = "kernel-devel-puppet" repo_fixture_path = File.join(File.dirname(__FILE__), '..', '..', '..', 'fixtures', 'el-repo') repo_content = <<-REPO [local] name=EL-releasever - test packages baseurl=file:///tmp/el-repo enabled=1 gpgcheck=0 protect=1 REPO agents.each do |agent| initially_installed_versions = [] scp_to(agent, repo_fixture_path, '/tmp') file_manifest = resource_manifest('file', '/etc/yum.repos.d/local.repo', ensure: 'present', content: repo_content) apply_manifest_on(agent, file_manifest) teardown do on(agent, 'rm -rf /tmp/el-repo') on(agent, 'rm -f /etc/yum.repos.d/local.repo') available_versions = on(agent, "yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | awk '{print $2}'").stdout initially_installed_versions.each do |version| if available_versions.include? version package_manifest = resource_manifest('package', package, ensure: version, install_only: true) apply_manifest_on(agent, package_manifest, :catch_failures => true) end end end step "Uninstall package versions for clean setup" do initially_installed_versions = on(agent, "yum --showduplicates list #{package} | sed -e '1,/Installed Packages/ d' -e '/Available Packages/,$ d' | awk '{print $2}'").stdout.split("\n") package_manifest = resource_manifest('package', package, ensure: 'absent', install_only: true) apply_manifest_on(agent, package_manifest, :catch_failures => true) do remaining_installed_versions = on(agent, "yum --showduplicates list #{package} | sed -e '1,/Installed Packages/ d' -e '/Available Packages/,$ d' | awk '{print $2}'").stdout assert(remaining_installed_versions.empty?) end available_versions = on(agent, "yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | awk '{print $2}'").stdout.split("\n") if available_versions.size < 2 skip_test "we need at least two package versions to perform the multiversion rpm test" end end step "Ensure oldest version of multiversion package is installed" do oldest_version = on(agent, "yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | head -1 | awk '{print $2}'").stdout.strip package_manifest = resource_manifest('package', package, ensure: oldest_version, install_only: true) apply_manifest_on(agent, package_manifest, :catch_failures => true) do installed_version = on(agent, "rpm -q #{package}").stdout assert_match(oldest_version, installed_version) end end step "Ensure newest package multiversion package in installed" do newest_version = on(agent, "yum --showduplicates list #{package} | sed -e '1,/Available Packages/ d' | tail -1 | awk '{print $2}'").stdout.strip package_manifest = resource_manifest('package', package, ensure: newest_version, install_only: true) apply_manifest_on(agent, package_manifest, :catch_failures => true) do installed_version = on(agent, "rpm -q #{package}").stdout assert_match(newest_version, installed_version) end end step "Ensure rpm will uninstall multiversion package" do package_manifest = resource_manifest('package', package, ensure: 'absent', install_only: true) apply_manifest_on(agent, package_manifest, :catch_failures => true) do remaining_installed_versions = on(agent, "yum --showduplicates list #{package} | sed -e '1,/Installed Packages/ d' -e '/Available Packages/,$ d' | awk '{print $2}'").stdout assert(remaining_installed_versions.empty?) end end end end puppetlabs-puppet-789f600/acceptance/tests/provider/package/yum_semantic_versioning.rb000066400000000000000000000060441470131746300314450ustar00rootroot00000000000000test_name "yum provider should use semantic versioning for ensuring desired version" do confine :to, :platform => /el-7/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = 'yum-utils' lower_package_version = '1.1.31-34.el7' middle_package_version = '1.1.31-42.el7' higher_package_version = '1.1.31-45.el7' agents.each do |agent| yum_command = 'yum' step "Setup: Skip test if box already has the package installed" do on(agent, "rpm -q #{package}", :acceptable_exit_codes => [1,0]) do |result| skip_test "package #{package} already installed on this box" unless result.output =~ /package #{package} is not installed/ end end step "Setup: Skip test if package versions are not available" do on(agent, "yum list #{package} --showduplicates", :acceptable_exit_codes => [1,0]) do |result| versions_available = [lower_package_version, middle_package_version, higher_package_version].all? { |needed_versions| result.output.include? needed_versions } skip_test "package #{package} versions not available on the box" unless versions_available end end step "Using semantic versioning to downgrade to a desired version <= X" do on(agent, "#{yum_command} install #{package} -y") package_manifest = resource_manifest('package', package, { ensure: "<=#{lower_package_version}", provider: 'yum' } ) apply_manifest_on(agent, package_manifest, :catch_failures => true) do installed_version = on(agent, "rpm -q #{package}").stdout assert_match(/#{lower_package_version}/, installed_version) end # idempotency test package_manifest = resource_manifest('package', package, { ensure: "<=#{lower_package_version}", provider: 'yum' } ) apply_manifest_on(agent, package_manifest, :catch_changes => true) on(agent, "#{yum_command} remove #{package} -y") end step "Using semantic versioning to ensure a version >X <=Y" do on(agent, "#{yum_command} install #{package} -y") package_manifest = resource_manifest('package', package, { ensure: ">#{lower_package_version} <=#{higher_package_version}", provider: 'yum' } ) apply_manifest_on(agent, package_manifest) do installed_version = on(agent, "rpm -q #{package}").stdout assert_match(/#{higher_package_version}/, installed_version) end on(agent, "#{yum_command} remove #{package} -y") end step "Using semantic versioning to install a version >X #{lower_package_version} <#{higher_package_version}", provider: 'yum' } ) # installing a version >X /sles/ tag 'audit:high' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::PackageUtils extend Puppet::Acceptance::ManifestUtils package = "helloworld" available_package_versions = ['1.0-2', '1.19-2', '2.0-2'] repo_fixture_path = File.join(File.dirname(__FILE__), '..', '..', '..', 'fixtures', 'sles-repo') repo_content = <<-REPO [local] name=local - test packages baseurl=file:///tmp/sles-repo enabled=1 gpgcheck=0 REPO agents.each do |agent| scp_to(agent, repo_fixture_path, '/tmp') file_manifest = resource_manifest('file', '/etc/zypp/repos.d/local.repo', ensure: 'present', content: repo_content) apply_manifest_on(agent, file_manifest) teardown do package_absent(agent, package, '--force-yes') file_manifest = resource_manifest('file', '/etc/zypp/repos.d/local.repo', ensure: 'absent') apply_manifest_on(agent, file_manifest) on(agent, 'rm -rf /tmp/sles-repo') end step "Ensure that package is installed first if not present" do package_manifest = resource_manifest('package', package, ensure: "<=#{available_package_versions[1]}") apply_manifest_on(agent, package_manifest) installed_package_version = on(agent, "rpm -q #{package}").stdout assert_match(available_package_versions[1], installed_package_version) end step "Ensure that package is updated" do package_manifest = resource_manifest('package', package, ensure: ">#{available_package_versions[1]}") apply_manifest_on(agent, package_manifest) installed_package_version = on(agent, "rpm -q #{package}").stdout assert_match(available_package_versions[2], installed_package_version) end end end puppetlabs-puppet-789f600/acceptance/tests/reports/000077500000000000000000000000001470131746300224255ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/reports/agent_sends_json_report_for_cached_catalog.rb000066400000000000000000000017761470131746300335320ustar00rootroot00000000000000test_name "C100533: Agent sends json report for cached catalog" do tag 'risk:high', 'audit:high', 'audit:integration', 'server' with_puppet_running_on(master, :main => {}) do expected_format = 'json' step "Perform agent run to ensure that catalog is cached" do agents.each do |agent| on(agent, puppet('agent', '-t'), :acceptable_exit_codes => [0,2]) end end step "Ensure agent sends #{expected_format} report for cached catalog" do agents.each do |agent| on(agent, puppet('agent', '-t', '--http_debug'), :acceptable_exit_codes => [0,2]) do |res| # Expected content-type should be in the headers of the # HTTP report payload being PUT to the server by the agent. unless res.stderr =~ /<- "PUT \/puppet\/v[3-9]\/report.*Content-Type: .*\/#{expected_format}/ fail_test("Report was not submitted in #{expected_format} format") end end end end end end puppetlabs-puppet-789f600/acceptance/tests/reports/cached_catalog_status_in_report.rb000066400000000000000000000046111470131746300313410ustar00rootroot00000000000000test_name "PUP-5867: The report specifies whether a cached catalog was used, and if so, why" do tag 'audit:high', 'audit:integration', 'server' master_reportdir = create_tmpdir_for_user(master, 'report_dir') teardown do on(master, "rm -rf #{master_reportdir}") end def remove_reports_on_master(master_reportdir, agent_node_name) on(master, "rm -rf #{master_reportdir}/#{agent_node_name}/*") end with_puppet_running_on(master, :master => { :reportdir => master_reportdir, :reports => 'store' }) do agents.each do |agent| step "cached_catalog_status should be 'not used' when a new catalog is retrieved" do step "Initial run: cache a newly retrieved catalog" do on(agent, puppet("agent", "-t"), :acceptable_exit_codes => [0,2]) end step "Run again and ensure report indicates that the cached catalog was not used" do on(agent, puppet("agent", "--onetime", "--no-daemonize"), :acceptable_exit_codes => [0, 2]) on(master, "cat #{master_reportdir}/#{agent.node_name}/*") do |result| assert_match(/cached_catalog_status: not_used/, result.stdout, "expected to find 'cached_catalog_status: not_used' in the report") end remove_reports_on_master(master_reportdir, agent.node_name) end end step "Run with --use_cached_catalog and ensure report indicates cached catalog was explicitly requested" do on(agent, puppet("agent", "--onetime", "--no-daemonize", "--use_cached_catalog"), :acceptable_exit_codes => [0, 2]) on(master, "cat #{master_reportdir}/#{agent.node_name}/*") do |result| assert_match(/cached_catalog_status: explicitly_requested/, result.stdout, "expected to find 'cached_catalog_status: explicitly_requested' in the report") end remove_reports_on_master(master_reportdir, agent.node_name) end step "On a run which fails to retrieve a new catalog, ensure report indicates cached catalog was used on failure" do on(agent, puppet("agent", "--onetime", "--no-daemonize", "--report_server #{master}", "--server nonexist"), :acceptable_exit_codes => [0, 2]) on(master, "cat #{master_reportdir}/#{agent.node_name}/*") do |result| assert_match(/cached_catalog_status: on_failure/, result.stdout, "expected to find 'cached_catalog_status: on_failure' in the report") end end end end end puppetlabs-puppet-789f600/acceptance/tests/reports/corrective_change_new_resource.rb000066400000000000000000000064351470131746300312140ustar00rootroot00000000000000test_name "C98092 - a new resource should not be reported as a corrective change" do require 'yaml' require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/agent_fqdn_utils' extend Puppet::Acceptance::AgentFqdnUtils tag 'audit:high', 'audit:integration', 'audit:refactor', # Uses a server currently but is testing agent report 'broken:images' test_file_name = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, test_file_name) tmp_file = {} agents.each do |agent| tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment) end teardown do step 'clean out produced resources' do agents.each do |agent| if tmp_file.has_key?(agent_to_fqdn(agent)) && tmp_file[agent_to_fqdn(agent)] != '' on(agent, "rm '#{tmp_file[agent_to_fqdn(agent)]}'", :accept_all_exit_codes => true) end on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end end step 'create file resource - site.pp to verify corrective change flag' do file_contents = 'this is a test' manifest = <<-MANIFEST file { '#{environmentpath}/#{tmp_environment}/manifests/site.pp': ensure => file, content => ' \$test_path = \$facts["networking"]["fqdn"] ? #{tmp_file} file { \$test_path: content => @(UTF8) #{file_contents} | UTF8 } ', } MANIFEST apply_manifest_on(master, manifest, :catch_failures => true) end step 'run agent(s)' do with_puppet_running_on(master, {}) do agents.each do |agent| #Run agent once to create new File resource step 'Run agent once to create new File resource' do on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 2) end #Verify the file resource is created step 'Verify the file resource is created' do on(agent, "cat '#{tmp_file[agent_to_fqdn(agent)]}'").stdout do |file_result| assert_equal(file_contents, file_result, 'file contents did not match accepted') end end end end end # Open last_run_report.yaml step 'Check report' do agents.each do |agent| on(agent, puppet('config print statedir')) do |command_result| report_path = command_result.stdout.chomp + '/last_run_report.yaml' on(agent, "cat '#{report_path}'").stdout do |report_contents| yaml_data = YAML::parse(report_contents) # Remove any Ruby class tags from the yaml yaml_data.root.each do |o| if o.respond_to?(:tag=) and o.tag != nil and o.tag.start_with?("!ruby") o.tag = nil end end report_yaml = yaml_data.to_ruby file_resource_details = report_yaml["resource_statuses"]["File[#{tmp_file[agent_to_fqdn(agent)]}]"] assert(file_resource_details.has_key?("corrective_change"), 'corrective_change key is missing') corrective_change_value = file_resource_details["corrective_change"] assert_equal(false, corrective_change_value, 'corrective_change flag should be false') end end end end end puppetlabs-puppet-789f600/acceptance/tests/reports/corrective_change_outside_puppet.rb000066400000000000000000000077711470131746300315710ustar00rootroot00000000000000test_name "C98093 - a resource changed outside of Puppet will be reported as a corrective change" do require 'yaml' require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/agent_fqdn_utils' extend Puppet::Acceptance::AgentFqdnUtils tag 'audit:high', 'audit:integration', 'audit:refactor', # Uses a server currently, but is testing agent report 'broken:images' test_file_name = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, test_file_name) tmp_file = {} agents.each do |agent| tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment) end teardown do step 'clean out produced resources' do agents.each do |agent| if tmp_file.has_key?(agent_to_fqdn(agent)) && tmp_file[agent_to_fqdn(agent)] != '' on(agent, "rm '#{tmp_file[agent_to_fqdn(agent)]}'", :accept_all_exit_codes => true) end on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end end step 'create file resource - site.pp to verify corrective change flag' do file_contents = 'this is a test' manifest = <<-MANIFEST file { '#{environmentpath}/#{tmp_environment}/manifests/site.pp': ensure => file, content => ' \$test_path = \$facts["networking"]["fqdn"] ? #{tmp_file} file { \$test_path: content => @(UTF8) #{file_contents} | UTF8 } ', } MANIFEST apply_manifest_on(master, manifest, :catch_failures => true) end step 'run agent(s)' do with_puppet_running_on(master, {}) do agents.each do |agent| #Run agent once to create new File resource step 'Run agent once to create new File resource' do on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 2) end #Verify the file resource is created step 'Verify the file resource is created' do on(agent, "cat '#{tmp_file[agent_to_fqdn(agent)]}'").stdout do |file_result| assert_equal(file_contents, file_result, 'file contents did not match accepted') end end #Delete the file step 'Delete the file' do on(agent, "rm '#{tmp_file[agent_to_fqdn(agent)]}'", :accept_all_exit_codes => true) end #Run agent to correct the file's absence step 'Run agent to correct the files absence' do on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 2) end #Verify the file resource is created step 'Verify the file resource is created' do on(agent, "cat '#{tmp_file[agent_to_fqdn(agent)]}'").stdout do |file_result| assert_equal(file_contents, file_result, 'file contents did not match accepted') end end end end end # Open last_run_report.yaml step 'Check report' do agents.each do |agent| on(agent, puppet('config print statedir')) do |command_result| report_path = command_result.stdout.chomp + '/last_run_report.yaml' on(agent, "cat '#{report_path}'").stdout do |report_contents| yaml_data = YAML::parse(report_contents) # Remove any Ruby class tags from the yaml yaml_data.root.each do |o| if o.respond_to?(:tag=) and o.tag != nil and o.tag.start_with?("!ruby") o.tag = nil end end report_yaml = yaml_data.to_ruby file_resource_details = report_yaml["resource_statuses"]["File[#{tmp_file[agent_to_fqdn(agent)]}]"] assert(file_resource_details.has_key?("corrective_change"), 'corrective_change key is missing') corrective_change_value = file_resource_details["corrective_change"] assert_equal(true, corrective_change_value, 'corrective_change flag should be true') end end end end end puppetlabs-puppet-789f600/acceptance/tests/reports/corrective_change_via_puppet.rb000066400000000000000000000104101470131746300306540ustar00rootroot00000000000000test_name "C98094 - a resource changed via Puppet manifest will not be reported as a corrective change" do require 'yaml' require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/agent_fqdn_utils' extend Puppet::Acceptance::AgentFqdnUtils tag 'audit:high', 'audit:integration', 'audit:refactor', # Uses a server currently, but is testing agent report 'broken:images', 'server' test_file_name = File.basename(__FILE__, '.*') tmp_environment = mk_tmp_environment_with_teardown(master, test_file_name) tmp_file = {} original_test_data = 'this is my original important data' modified_test_data = 'this is my modified important data' agents.each do |agent| tmp_file[agent_to_fqdn(agent)] = agent.tmpfile(tmp_environment) end teardown do # Remove all traces of the last used environment agents.each do |agent| on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end step 'clean out produced resources' do agents.each do |agent| if tmp_file.has_key?(agent_to_fqdn(agent)) && tmp_file[agent_to_fqdn(agent)] != '' on(agent, "rm '#{tmp_file[agent_to_fqdn(agent)]}'", :accept_all_exit_codes => true) end end end end def create_manifest_for_file_resource(file_resource, file_contents, environment_name) manifest = <<-MANIFEST file { '#{environmentpath}/#{environment_name}/manifests/site.pp': ensure => file, content => ' \$test_path = \$facts["networking"]["fqdn"] ? #{file_resource} file { \$test_path: content => @(UTF8) #{file_contents} | UTF8 } ', } MANIFEST apply_manifest_on(master, manifest, :catch_failures => true) end step 'create file resource in site.pp' do create_manifest_for_file_resource(tmp_file, original_test_data, tmp_environment) end step 'run agent(s) to create the new resource' do with_puppet_running_on(master, {}) do agents.each do |agent| step 'Run agent once to create new File resource' do on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 2) end step 'Verify the file resource is created' do on(agent, "cat '#{tmp_file[agent_to_fqdn(agent)]}'").stdout do |file_contents| assert_equal(original_test_data, file_contents, 'file contents did not match expected contents') end end end step 'Change the manifest for the resource' do create_manifest_for_file_resource(tmp_file, modified_test_data, tmp_environment) end agents.each do |agent| step 'Run agent a 2nd time to change the File resource' do on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 2) end step 'Verify the file resource is created' do on(agent, "cat '#{tmp_file[agent_to_fqdn(agent)]}'").stdout do |file_contents| assert_equal(modified_test_data, file_contents, 'file contents did not match expected contents') end end end end end # Open last_run_report.yaml step 'Check report' do agents.each do |agent| on(agent, puppet('config print statedir')) do |command_result| report_path = command_result.stdout.chomp + '/last_run_report.yaml' on(agent, "cat '#{report_path}'").stdout do |report_contents| yaml_data = YAML::parse(report_contents) # Remove any Ruby class tags from the yaml yaml_data.root.each do |o| if o.respond_to?(:tag=) and o.tag != nil and o.tag.start_with?("!ruby") o.tag = nil end end report_yaml = yaml_data.to_ruby file_resource_details = report_yaml["resource_statuses"]["File[#{tmp_file[agent_to_fqdn(agent)]}]"] assert(file_resource_details.has_key?("corrective_change"), 'corrective_change key is missing') corrective_change_value = file_resource_details["corrective_change"] assert_equal(false, corrective_change_value, 'corrective_change flag for the changed resource should be false') end end end end end puppetlabs-puppet-789f600/acceptance/tests/reports/submission.rb000066400000000000000000000041661470131746300251540ustar00rootroot00000000000000test_name "Report submission" tag 'audit:high', 'audit:integration' if master.is_pe? require "time" def puppetdb hosts.detect { |h| h['roles'].include?('database') } end def sleep_until_queue_empty(timeout=60) metric = "org.apache.activemq:BrokerName=localhost,Type=Queue,Destination=com.puppetlabs.puppetdb.commands" queue_size = nil begin Timeout.timeout(timeout) do until queue_size == 0 result = on(puppetdb, %Q{curl http://localhost:8080/v3/metrics/mbean/#{CGI.escape(metric)}}) if md = /"?QueueSize"?\s*:\s*(\d+)/.match(result.stdout.chomp) queue_size = Integer(md[1]) end sleep 1 end end rescue Timeout::Error raise "Queue took longer than allowed #{timeout} seconds to empty" end end def query_last_report_time_on(agent) time_query_script = <<-EOS require "net/http" require "json" puppetdb_url = URI("http://localhost:8080/v3/reports") puppetdb_url.query = CGI.escape(%Q{query=["=","certname","#{agent}"]}) result = Net::HTTP.get(puppetdb_url) json = JSON.load(result) puts json.first["receive-time"] EOS on(puppetdb, "#{master[:privatebindir]}/ruby -e '#{time_query_script}'").output.chomp end last_times = {} agents.each do |agent| last_times[agent] = query_last_report_time_on(agent) end with_puppet_running_on(master, {}) do agents.each do |agent| on(agent, puppet('agent', "-t")) sleep_until_queue_empty current_time = Time.parse(query_last_report_time_on(agent)) last_time = Time.parse(last_times[agent]) assert(current_time > last_time, "Most recent report time #{current_time} is not newer than last report time #{last_time}") end end else testdir = create_tmpdir_for_user master, 'report_submission' teardown do on master, "rm -rf #{testdir}" end with_puppet_running_on(master, :main => { :reportdir => testdir, :reports => 'store' }) do agents.each do |agent| on(agent, puppet('agent', "-t")) on master, "grep -q #{agent.node_name} #{testdir}/*/*" end end end puppetlabs-puppet-789f600/acceptance/tests/resource/000077500000000000000000000000001470131746300225565ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/exec/000077500000000000000000000000001470131746300235025ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/exec/accept_array_commands.rb000066400000000000000000000010651470131746300303470ustar00rootroot00000000000000test_name "Be able to execute array commands" do tag 'audit:high', 'audit:acceptance' agents.each do |agent| if agent.platform =~ /windows/ cmd = ['C:\Windows\System32\cmd.exe', '/c', 'echo', '*'] else cmd = ['/bin/echo', '*'] end exec_manifest = <<~MANIFEST exec { "test exec": command => #{cmd}, logoutput => true, } MANIFEST apply_manifest_on(agent, exec_manifest) do |output| assert_match('Notice: /Stage[main]/Main/Exec[test exec]/returns: *', output.stdout) end end end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/accept_multi-line_commands.rb000066400000000000000000000013741470131746300313130ustar00rootroot00000000000000test_name "Be able to execute multi-line commands (#9996)" confine :except, :platform => 'windows' tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| temp_file_name = agent.tmpfile('9996-multi-line-commands') test_manifest = < "/bin/echo '#Test' > #{temp_file_name}; /bin/echo 'bob' >> #{temp_file_name};" } HERE expected_results = <64KB file to exceed pipe buffer. lorem_ipsum = < ['/bin', '/usr/bin', 'C:/cygwin32/bin', 'C:/cygwin64/bin', 'C:/cygwin/bin'], logoutput => true}") do |result| fail_test "didn't seem to run the command" unless result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja' fail_test "didn't print output correctly" unless result.stdout.lines.select {|line| line =~ /\/returns:/}.count == 4097 end apply_manifest_on(agent, "exec {'echo': path => ['/bin', '/usr/bin', 'C:/cygwin32/bin', 'C:/cygwin64/bin', 'C:/cygwin/bin'], logoutput => true}") do |result| fail_test "didn't seem to run the command" unless result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja' end end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/should_not_run_command_creates.rb000066400000000000000000000025201470131746300322740ustar00rootroot00000000000000test_name "should not run command creates" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| touch = agent.tmpfile('touched') donottouch = agent.tmpfile('not-touched') manifest = %Q{ exec { "test#{Time.new.to_i}": command => '#{agent.touch(donottouch)}', creates => "#{touch}"} } step "prepare the agents for the test" on agent, "touch #{touch} && rm -f #{donottouch}" step "test using puppet apply" apply_manifest_on(agent, manifest) do |result| fail_test "looks like the thing executed, which it shouldn't" if result.stdout.include? 'executed successfully' end step "verify the file didn't get created" on agent, "test -f #{donottouch}", :acceptable_exit_codes => [1] step "prepare the agents for the second part of the test" on agent, "touch #{touch} ; rm -f #{donottouch}" step "test using puppet resource" on(agent, puppet_resource('exec', "test#{Time.new.to_i}", "command='#{agent.touch(donottouch)}'", "creates='#{touch}'")) do |result| fail_test "looks like the thing executed, which it shouldn't" if result.stdout.include? 'executed successfully' end step "verify the file didn't get created the second time" on agent, "test -f #{donottouch}", :acceptable_exit_codes => [1] end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/should_run_bad_command.rb000066400000000000000000000044751470131746300305270ustar00rootroot00000000000000test_name "tests that puppet can run badly written scripts that fork and inherit descriptors" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' def sleepy_daemon_script(agent) if agent['platform'] =~ /win/ # Windows uses a shorter sleep, because it's expected to wait until the end. return < true}") do |result| fail_test "didn't seem to run the command" unless result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja' end end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/should_run_command.rb000066400000000000000000000020541470131746300277100ustar00rootroot00000000000000test_name "tests that puppet correctly runs an exec." # original author: Dan Bode --daniel 2010-12-23 tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' def before(agent) step "file to be touched should not exist." agent.tmpfile('test-exec') end def after(agent, touched) step "checking the output worked" on agent, "test -f #{touched}" step "clean up the system" on agent, "rm -f #{touched}" end agents.each do |agent| touched = before(agent) apply_manifest_on(agent, "exec {'test': command=>'#{agent.touch(touched)}'}") do |result| fail_test "didn't seem to run the command" unless result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja' end after(agent, touched) touched = before(agent) on(agent, puppet_resource('-d', 'exec', 'test', "command='#{agent.touch(touched)}'}")) do |result| fail_test "didn't seem to run the command" unless result.stdout.include? 'executed successfully' unless agent['locale'] == 'ja' end after(agent, touched) end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/should_run_command_as_user.rb000066400000000000000000000034021470131746300314270ustar00rootroot00000000000000test_name "The exec resource should be able to run commands as a different user" do confine :except, :platform => 'windows' tag 'audit:high', 'audit:acceptance' require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::BeakerUtils def random_username "pl#{rand(999999).to_i}" end def exec_resource_manifest(params = {}) default_params = { :logoutput => true, :path => '/usr/bin:/usr/sbin:/bin:/sbin', :command => 'echo Hello' } params = default_params.merge(params) params_str = params.map do |param, value| value_str = value.to_s value_str = "'#{value_str}'" if value.is_a?(String) " #{param} => #{value_str}" end.join(",\n") <<-MANIFEST exec { 'run_test_command': #{params_str} } MANIFEST end agents.each do |agent| username = random_username # Create our user. Ensure that we start with a clean slate. agent.user_absent(username) agent.user_present(username) teardown { agent.user_absent(username) } tmpdir = agent.tmpdir("forbidden") on(agent, "chmod 700 #{tmpdir}") step "Runs the command even when the user doesn't have permissions to access the pwd" do # Can't use apply_manifest_on here because that does not take the :cwd # as an option. tmpfile = agent.tmpfile("exec_user_perms_manifest") create_remote_file(agent, tmpfile, exec_resource_manifest(user: username)) on(agent, "cd #{tmpdir} && puppet apply #{tmpfile} --detailed-exitcodes", acceptable_exit_codes: [0, 2]) end step "Runs the command even when the user doesn't have permission to access the specified cwd" do apply_manifest_on(agent, exec_resource_manifest(user: username, cwd: tmpdir), catch_failures: true) end end end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/should_run_command_in_cwd.rb000066400000000000000000000220001470131746300312240ustar00rootroot00000000000000test_name "The Exec resource should run commands in the specified cwd" do tag 'audit:high', 'audit:acceptance' confine :except, :platform => /debian-12-amd64/ # PUP-12020 require 'puppet/acceptance/windows_utils' extend Puppet::Acceptance::WindowsUtils # Useful utility that converts a string literal # to a regex. We do a lot of assertions on file # paths here that we need to escape, so this is # a nice way of making the code more readable. def to_regex(str) Regexp.new(Regexp.escape(str)) end def exec_resource_manifest(command, params = {}) default_params = { :command => command } params = default_params.merge(params) params_str = params.map do |param, value| value_str = value.to_s # Single quote the strings in case our value is a Windows # path value_str = "'#{value_str}'" if value.is_a?(String) " #{param} => #{value_str}" end.join(",\n") <<-MANIFEST exec { 'run_test_command': #{params_str} } MANIFEST end def assert_file_on(host, filepath, failure_comment) if host.platform =~ /windows/ cmd = "cmd.exe /c \"type #{filepath.gsub('/', '\\')}\"" else cmd = "test -f #{filepath}" end on(host, cmd, :acceptable_exit_codes => [0, 1]) do |result| assert_equal(0, result.exit_code, failure_comment) end end agents.each do |agent| testdir = agent.tmpdir("mock_testdir") if agent.platform =~ /windows/ path = 'C:\Windows\System32' echo_to = 'cmd.exe /c echo testing >' cat = 'cmd.exe /c type' non_existant_dir = 'C:\does_not_exist' origin_working_dir = on(agent, 'cmd.exe /c echo %CD%').stdout.chomp else path = '/usr/bin:/usr/sbin:/bin:/sbin' echo_to = 'echo testing >' cat = 'cat' non_existant_dir = '/does_not_exist' origin_working_dir = on(agent, 'pwd').stdout.chomp end step "clean current working directory" do on(agent, "rm -f cwd_test*") end step "Defaults to the current directory if the CWD option is not provided" do apply_manifest_on(agent, exec_resource_manifest("#{echo_to} cwd_test1", {:path => path}), :catch_failures => true) assert_file_on(agent, File.join(origin_working_dir, 'cwd_test1'), 'Exec did not create file in origin pwd, exec resource not defaulting to pwd when no :cwd option is given') end step "Runs the command in the user specified CWD" do apply_manifest_on(agent, exec_resource_manifest("#{echo_to} cwd_test2", {:cwd => testdir, :path => path}), :catch_failures => true) assert_file_on(agent, File.join(testdir, 'cwd_test2'), 'Exec did not create file in test directory, exec resource not using :cwd given') end step "Errors if the user specified CWD does not exist" do apply_manifest_on(agent, exec_resource_manifest("#{echo_to} cwd_test3", {cwd: non_existant_dir, :path => path}), :expect_failures => true) do |result| assert_equal(4, result.exit_code, "Exec manifest still executed with non-existant :cwd") end end # "onlyif" testing will require some form of runnable test in the testdir for the # onlyif clause to actually execute. The runnable test we will use is attempting to # 'cat' an unqualified file that will only exist in the testdir create_remote_file(agent, File.join(testdir, 'testdir_onlyif.txt'), 'testing') step 'Runs a "check" command (:onlyif or :unless) in the user specified CWD' do apply_manifest_on(agent, exec_resource_manifest("#{echo_to} cwd_test4", {cwd: testdir, :path => path, :onlyif => "#{cat} testdir_onlyif.txt"}), :expect_changes => true) assert_file_on(agent, File.join(testdir, 'cwd_test4'), 'Exec did not create file in test directory, exec resource not using :cwd given') end step 'Does not run the exec if the "check" command (:onlyif or :unless) fails' do apply_manifest_on(agent, exec_resource_manifest("#{echo_to} cwd_test5", {cwd: testdir, :path => path, :onlyif => "foobar"}), :expect_failures => true) do |result| assert_equal(4, result.exit_code, "Exec manifest still executed with failed :onlyif clause") end end tmpdir_noaccess = agent.tmpdir("mock_dir") create_remote_file(agent, File.join(tmpdir_noaccess, 'noaccess.txt'), 'foobar') username = "pl#{rand(999999).to_i}" # The next two steps set up to test running with a CWD that the user does not have access to. # The setup for the test creates 1. a new user and 2. a new directory that the new user does # not have access to. step "Setup user for 'no access' test" do agent.user_present(username) if agent.platform =~ /solaris/ # for some reason applications of 'user_present' on solaris 10 don't manage the homedir correctly, so just # force a puppet apply to manage the user on agent, puppet_resource('user', username, "ensure=present managehome=true home=/export/home/#{username}") # we need to create the user directory ourselves in order for solaris users to successfully login on(agent, "mkdir /export/home/#{username} && chown -R #{username} /export/home/#{username}") elsif agent.platform =~ /osx/ # we need to create the user directory ourselves in order for macos users to successfully login on(agent, "mkdir /Users/#{username} && chown -R #{username}:80 /Users/#{username}") elsif agent.platform =~ /debian|ubuntu|sles/ # we need to create the user directory ourselves in order for deb users to successfully login on(agent, "mkdir /home/#{username} && chown -R #{username} /home/#{username}") end teardown { agent.user_absent(username) } end tmpdir_noaccess = agent.tmpdir("mock_noaccess") create_remote_file(agent, File.join(tmpdir_noaccess, 'noaccess.txt'), 'foobar') step "Setup restricted access directory for 'no access' test" do if agent.platform =~ /windows/ deny_administrator_access_to(agent, tmpdir_noaccess) deny_administrator_access_to(agent, File.join(tmpdir_noaccess, 'noaccess.txt')) else if agent.platform =~ /osx/ # This is a little nuts, but on MacOS the tmpdir returned from agent.tmpdir is located in # a directory that users other than root can't even access, i.e. other users won't have access # to either the noaccess dir itself (which we want) _or the tmpdir root it's located in_. This is # a problem since it will look to puppet like the noacceess dir doesn't exist at all, and so we # can't count on any reliaable failure since we want a return indicating no access, not a missing directory. # # To get around this for MacOS platforms we simply use the new user's homedir as the 'tmpdir' and # put the noaccess dir there. on(agent, "mkdir /Users/#{username}/noaccess_test && cp #{tmpdir_noaccess}/noaccess.txt /Users/#{username}/noaccess_test && chmod -R 600 /Users/#{username}/noaccess_test") tmpdir_noaccess = "/Users/#{username}/noaccess_test" end # remove permissions for all other users other than root, which should force puppet to fail when running as another user on(agent, "chmod -R 600 #{tmpdir_noaccess}") end end step "Errors if the user does not have access to the specified CWD" do manifest_path = agent.tmpfile('apply_manifest.pp') create_remote_file(agent, manifest_path, exec_resource_manifest("#{cat} noaccess.txt", {:cwd => tmpdir_noaccess, :path => path})) if agent.platform =~ /windows/ on(agent, "cmd.exe /c \"puppet apply #{manifest_path} --detailed-exitcodes\"", :acceptable_exit_codes => [4]) do |result| assert_equal(4, result.exit_code, "Exec manifest still executed inside restricted directory", ) end elsif agent.platform =~ /osx/ # on MacOS we need to copy the manifest to run to the user's home dir and give the user ownership. otherwise puppet won't run on it. on(agent, "cp #{manifest_path} /Users/#{username}/noaccess_manifest.pp && chown #{username}:80 /Users/#{username}/noaccess_manifest.pp") on(agent, "su - #{username} -c \"/opt/puppetlabs/bin/puppet apply /Users/#{username}/noaccess_manifest.pp --detailed-exitcodes\"", :acceptable_exit_codes => [4]) do |result| assert_equal(4, result.exit_code, "Exec manifest still executed inside restricted directory") end else on(agent, "chown #{username} #{manifest_path}") if agent.platform =~ /solaris|aix/ on(agent, "su - #{username} -c \"/opt/puppetlabs/bin/puppet apply #{manifest_path} --detailed-exitcodes\"", :acceptable_exit_codes => [4]) do |result| assert_equal(4, result.exit_code, "Exec manifest still executed inside restricted directory") end else on(agent, "su #{username} -c \"/opt/puppetlabs/bin/puppet apply #{manifest_path} --detailed-exitcodes\"", :acceptable_exit_codes => [4]) do |result| assert_equal(4, result.exit_code, "Exec manifest still executed inside restricted directory") end end end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/should_set_environment_variables.rb000066400000000000000000000071441470131746300326620ustar00rootroot00000000000000test_name "The Exec resource should set user-specified environment variables" do tag 'audit:high', 'audit:acceptance' # Would be nice to parse the actual values from puppet_output, # but that would require some complicated matching since # puppet_output contains other stuff. def assert_env_var_values(puppet_output, expected_values) expected_values.each do |env_var, value| assert_match(/#{env_var}=#{value}/, puppet_output, "Expected '#{env_var}=#{value}' to be printed as part of the output!") end end agents.each do |agent| # Calculate some top-level variables/functions we # will need for our tests. unless agent.platform =~ /windows/ path = '/usr/bin:/usr/sbin:/bin:/sbin' print_env_vars = lambda do |*env_vars| env_vars_str = env_vars.map do |env_var| "#{env_var}=$#{env_var}" end.join(" ") "echo #{env_vars_str}" end else # Powershell's directory is dependent on what version of Powershell is # installed on the system (e.g. v1.0, v2.0), so we need to programmatically # calculate the executable's directory to add to our PATH variable. powershell_path = on(agent, "cmd.exe /c where powershell.exe").stdout.chomp *powershell_dir, _ = powershell_path.split('\\') powershell_dir = powershell_dir.join('\\') path = "C:\Windows\System32;#{powershell_dir}" print_env_vars = lambda do |*env_vars| env_vars_str = env_vars.map do |env_var| "#{env_var}=$env:#{env_var}" end "powershell.exe \"Write-Host -NoNewLine #{env_vars_str}\"" end end # Easier to read than a def. The def. would require us # to specify the host as a param. in order to get the path # and print_cwd command, which is unnecessary clutter. exec_resource_manifest = lambda do |params = {}| default_params = { :logoutput => true, :path => path } params = default_params.merge(params) params_str = params.map do |param, value| value_str = value.to_s # Single quote the strings in case our value is a Windows # path value_str = "'#{value_str}'" if value.is_a?(String) " #{param} => #{value_str}" end.join(",\n") <<-MANIFEST exec { 'run_test_command': #{params_str} } MANIFEST end step 'Passes the user-specified environment variables into the command' do manifest = exec_resource_manifest.call( command: print_env_vars.call('ENV_VAR_ONE', 'ENV_VAR_TWO'), environment: ['ENV_VAR_ONE=VALUE_ONE', 'ENV_VAR_TWO=VALUE_TWO'] ) apply_manifest_on(agent, manifest) do |result| assert_env_var_values(result.stdout, ENV_VAR_ONE: 'VALUE_ONE', ENV_VAR_TWO: 'VALUE_TWO') end end step "Temporarily overrides previously set environment variables" do manifest = exec_resource_manifest.call( command: print_env_vars.call('ENV_VAR_ONE'), environment: ['ENV_VAR_ONE=VALUE_OVERRIDE'] ) apply_manifest_on(agent, manifest, environment: { 'ENV_VAR_ONE' => 'VALUE' }) do |result| assert_env_var_values(result.stdout, ENV_VAR_ONE: 'VALUE_OVERRIDE') end end step "Temporarily overrides previously set environment variables even if the passed-in value is empty" do manifest = exec_resource_manifest.call( command: print_env_vars.call('ENV_VAR_ONE'), environment: ['ENV_VAR_ONE='] ) apply_manifest_on(agent, manifest, environment: { 'ENV_VAR_ONE' => 'VALUE' }) do |result| assert_env_var_values(result.stdout, ENV_VAR_ONE: '') end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/exec/should_set_path.rb000066400000000000000000000011711470131746300272140ustar00rootroot00000000000000test_name "the path statement should work to locate commands" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| file = agent.tmpfile('touched-should-set-path') step "clean up the system for the test" on agent, "rm -f #{file}" step "invoke the exec resource with a path set" on(agent, puppet_resource('exec', 'test', "command='#{agent.touch(file, false)}'", "path='#{agent.path}'")) step "verify that the files were created" on agent, "test -f #{file}" step "clean up the system after testing" on agent, "rm -f #{file}" end puppetlabs-puppet-789f600/acceptance/tests/resource/file/000077500000000000000000000000001470131746300234755ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/file/ascii_diff_output_content_attribute.rb000066400000000000000000000033751470131746300333470ustar00rootroot00000000000000test_name "ASCII Diff Output of Content Attribute" do tag 'audit:high', 'audit:acceptance' sha256 = Digest::SHA256.new agents.each do |agent| step 'When handling ASCII files' do target = agent.tmpfile('content_ASCII_file_test') initial_text = 'Initial Text' initial_text_sha_checksum = sha256.hexdigest(initial_text) updated_text = 'Updated Text' updated_text_sha_checksum = sha256.hexdigest(updated_text) on agent, puppet('config', 'set', 'diff', 'diff') step 'Ensure the test environment is clean' do on agent, "rm -f #{target}" end teardown do on agent, "rm -f #{target}" end step 'Create ASCII file using content' do manifest = "file { '#{target}': content => '#{initial_text}', ensure => present , checksum => 'sha256'}" on(agent, puppet('apply'), :stdin => manifest) do |result| assert_match(/ensure: defined content as '{sha256}#{initial_text_sha_checksum}'/, result.stdout, "#{agent}: checksum of ASCII file not matched") end end step 'Update existing ASCII file content' do manifest = "file { '#{target}': content => '#{updated_text}', ensure => present , checksum => 'sha256'}" on(agent, puppet('apply','--show_diff'), :stdin => manifest) do |result| assert_match(/content: content changed '{sha256}#{initial_text_sha_checksum}' to '{sha256}#{updated_text_sha_checksum}'/, result.stdout, "#{agent}: checksum of ASCII file not matched after update") assert_match(/^- ?#{initial_text}$/, result.stdout, "#{agent}: initial text not found in diff") assert_match(/^\+ ?#{updated_text}$/, result.stdout, "#{agent}: updated text not found in diff") end end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/bin_diff_output_content_attribute.rb000066400000000000000000000070731470131746300330260ustar00rootroot00000000000000test_name "Binary Diff Output of Content Attribute" do tag 'audit:high', 'audit:acceptance' # cannot test binary diff on windows2012r2_ja-64-1 # Error: Could not write report for afire-lien.delivery.puppetlabs.net at C:/ProgramData/PuppetLabs/puppet/cache/reports/afire-lien.delivery.puppetlabs.net/201912041455.yaml: anchor value must contain alphanumerical characters only # Error: Could not send report: anchor value must contain alphanumerical characters only confine :except, {}, hosts.select { |host| host[:platform]=~ /windows/ && host[:locale] == 'ja' } sha256 = Digest::SHA256.new agents.each do |agent| step 'When handling binary files' do target = agent.tmpfile('content_binary_file_test') initial_bin_data = "\xc7\xd1\xfc\x84" initial_base64_data = Base64.encode64(initial_bin_data).chomp initial_sha_checksum = sha256.hexdigest(initial_bin_data) updated_bin_data = "\xc7\xd1\xfc\x85" updated_base64_data = Base64.encode64(updated_bin_data).chomp updated_sha_checksum = sha256.hexdigest(updated_bin_data) on(agent, puppet('config', 'set', 'diff', 'diff')) agent_default_external_encoding=nil on(agent, "#{ruby_command(agent)} -e \"puts Encoding.default_external\"") do |result| agent_default_external_encoding = result.stdout.chomp end if agent_default_external_encoding && agent_default_external_encoding != Encoding.default_external begin initial_bin_data=initial_bin_data.force_encoding(agent_default_external_encoding).encode(Encoding.default_external) updated_bin_data=updated_bin_data.force_encoding(agent_default_external_encoding).encode(Encoding.default_external) rescue Encoding::InvalidByteSequenceError #depending on agent_default_external_encoding, the conversion may fail, but this should not be a problem end end teardown do on agent, "rm -f #{target}" end step 'Ensure the test environment is clean' do on agent, "rm -f #{target}" end step 'Create binary file using content' do manifest = "file { '#{target}': content => Binary('#{initial_base64_data}'), ensure => present , checksum => 'sha256'}" on(agent, puppet('apply'), :stdin => manifest) do |result| assert_match(/ensure: defined content as '{sha256}#{initial_sha_checksum}'/, result.stdout, "#{agent}: checksum of binary file not matched") end end step 'Update existing binary file content' do manifest = "file { '#{target}': content => Binary('#{updated_base64_data}'), ensure => present , checksum => 'sha256'}" on(agent, puppet('apply','--show_diff'), :stdin => manifest) do |result| assert_match(/content: content changed '{sha256}#{initial_sha_checksum}' to '{sha256}#{updated_sha_checksum}'/, result.stdout, "#{agent}: checksum of binary file not matched after update") refute_match(/content: Received a Log attribute with invalid encoding:/, result.stdout, "#{agent}: Received a Log attribute with invalid encoding") if initial_bin_data.valid_encoding? && updated_bin_data.valid_encoding? assert_match(/^- ?#{initial_bin_data}$/, result.stdout, "#{agent}: initial utf-8 data not found in binary diff") assert_match(/^\+ ?#{updated_bin_data}$/, result.stdout, "#{agent}: updated utf-8 data not found in binary diff") else assert_match(/Binary files #{target} and .* differ/, result.stdout, "#{agent}: Binary file diff notice not matched") end end end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/content_attribute.rb000066400000000000000000000055761470131746300275740ustar00rootroot00000000000000test_name "Content Attribute" tag 'audit:high', 'audit:refactor', # Use block stype test_name 'audit:acceptance' agents.each do |agent| target = agent.tmpfile('content_file_test') step "Ensure the test environment is clean" on agent, "rm -f #{target}" step "Content Attribute: using raw content" checksums_fips = ['sha256', 'sha256lite'] checksums_no_fips = ['sha256', 'sha256lite', 'md5', 'md5lite'] if on(agent, facter("fips_enabled")).stdout =~ /true/ checksums = checksums_fips else checksums = checksums_no_fips end manifest = "file { '#{target}': content => 'This is the test file content', ensure => present }" manifest += checksums.collect {|checksum_type| "file { '#{target+checksum_type}': content => 'This is the test file content', ensure => present, checksum => #{checksum_type} }" }.join("\n") apply_manifest_on(agent, manifest) do |result| checksums.each do |checksum_type| refute_match(/content changed/, result.stdout, "#{agent}: shouldn't have overwrote #{target+checksum_type}") end end on(agent, "cat #{target}") do |result| assert_match(/This is the test file content/, result.stdout, "File content not matched on #{agent}") unless agent['locale'] == 'ja' end step "Content Attribute: illegal timesteps" ['mtime', 'ctime'].each do |checksum_type| manifest = "file { '#{target+checksum_type}': content => 'This is the test file content', ensure => present, checksum => #{checksum_type} }" apply_manifest_on(agent, manifest, :acceptable_exit_codes => [1]) do |result| assert_match(/Error: Validation of File\[#{target+checksum_type}\] failed: You cannot specify content when using checksum '#{checksum_type}'/, result.stderr, "#{agent}: expected failure") unless agent['locale'] == 'ja' end end step "Ensure the test environment is clean" on(agent, "rm -f #{target}") step "Content Attribute: using a checksum from filebucket" on(agent, "echo 'This is the checksum file contents' > #{target}") step "Backup file into the filebucket" on(agent, puppet_filebucket("backup --local #{target}")) step "Modify file to force apply to retrieve file from local clientbucket" on(agent, "echo 'This is the modified file contents' > #{target}") dir = on(agent, puppet_filebucket("--configprint clientbucketdir")).stdout.chomp sha256_manifest = %Q| filebucket { 'local': path => '#{dir}', } file { '#{target}': ensure => present, content => '{sha256}3b9238769b033b48073267b8baea00fa51c598dc14081da51f2e510c37c46a28', backup => local, } | step "Applying Manifest on Agent" apply_manifest_on agent, sha256_manifest step "Validate filebucket checksum file contents" on(agent, "cat #{target}") do |result| assert_match(/This is the checksum file content/, result.stdout, "File content not matched on #{agent}") unless agent['locale'] == 'ja' end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/handle_fifo_files.rb000066400000000000000000000035411470131746300274450ustar00rootroot00000000000000test_name "should be able to handle fifo files" tag 'audit:high', 'audit:acceptance' confine :except, :platform => /windows/ def ensure_content_to_file_manifest(file_path, ensure_value) return <<-MANIFEST file { "#{file_path}": ensure => #{ensure_value}, content => "Hello World" } MANIFEST end agents.each do |agent| tmp_path = agent.tmpdir("tmpdir") fifo_path = "#{tmp_path}/myfifo" teardown do agent.rm_rf(tmp_path) end step "create fifo" do on(agent, "mkfifo #{fifo_path}") end step "check that fifo got created" do on(agent, "ls -l #{fifo_path}") do |result| assert(result.stdout.start_with?('p')) end end step "puppet ensures given fifo is present" do apply_manifest_on(agent, ensure_content_to_file_manifest(fifo_path, 'present'), :acceptable_exit_codes => [2]) do |result| assert_match(/Warning: .+ Ensure set to :present but file type is fifo so no content will be synced/, result.stderr) end end step "check that given file is still a fifo" do on(agent, "ls -l #{fifo_path}") do |result| assert(result.stdout.start_with?('p')) end end step "puppet ensures given fifo is a regular file" do apply_manifest_on(agent, ensure_content_to_file_manifest(fifo_path, 'file'), :acceptable_exit_codes => [0]) do |result| assert_match(/Notice: .+\/myfifo\]\/ensure: defined content as '{/, result.stdout) refute_match(/Warning: .+ Ensure set to :present but file type is fifo so no content will be synced/, result.stderr) end end step "check that given fifo is now a regular file" do on(agent, "ls -l #{fifo_path}") do |result| assert(result.stdout.start_with?('-')) end end step "check that given file now has desired content" do on(agent, "cat #{fifo_path}") do |result| assert_equal('Hello World', result.stdout) end end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/handle_fifo_files_when_recursing.rb000066400000000000000000000036601470131746300325510ustar00rootroot00000000000000test_name "should be able to handle fifo files when recursing" tag 'audit:high', 'audit:acceptance' confine :except, :platform => /windows/ def ensure_owner_recursively_manifest(path, owner_value) return <<-MANIFEST file { "#{path}": ensure => present, recurse => true, owner => #{owner_value} } MANIFEST end agents.each do |agent| initial_owner = '' random_user = "pl#{rand(999).to_i}" tmp_path = agent.tmpdir("tmpdir") fifo_path = "#{tmp_path}/myfifo" teardown do agent.rm_rf(tmp_path) end step "create fifo file" do on(agent, "mkfifo #{fifo_path}") on(agent, puppet("resource user #{random_user} ensure=absent")) end step "check that fifo file got created" do on(agent, "ls -l #{fifo_path}") do |result| assert(result.stdout.start_with?('p')) initial_owner = result.stdout.split[2] end end step "create a new user" do on(agent, puppet("resource user #{random_user} ensure=present")) end step "puppet ensures '#{random_user}' as owner of path" do apply_manifest_on(agent, ensure_owner_recursively_manifest(tmp_path, random_user), :acceptable_exit_codes => [0]) do |result| assert_match(/#{tmp_path}\]\/owner: owner changed '#{initial_owner}' to '#{random_user}'/, result.stdout) refute_match(/Error: .+ Failed to generate additional resources using ‘eval_generate’: Cannot manage files of type fifo/, result.stderr) end end step "check that given file is still a fifo" do on(agent, "ls -l #{fifo_path}") do |result| assert(result.stdout.start_with?('p')) end end step "check ownership of fifo file" do on(agent, "ls -l #{fifo_path}") do |result| user = result.stdout.split[2] assert_equal(random_user, user) end end step "check ownership of tmp folder" do on(agent, "ls -ld #{tmp_path}") do |result| user = result.stdout.split[2] assert_equal(random_user, user) end end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/should_create_directory.rb000066400000000000000000000022031470131746300307240ustar00rootroot00000000000000test_name "should create directory" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| target = agent.tmpfile("create-dir") teardown do step "clean up after the test run" do on(agent, "rm -rf #{target}") end end step "verify we can create a directory" do on(agent, puppet_resource("file", target, 'ensure=directory')) end step "verify the directory was created" do on(agent, "test -d #{target}") end dir_manifest = agent.tmpfile("dir-resource") create_remote_file(agent, dir_manifest, <<-PP) $dir='#{target}' $same_dir='#{target}/' file {$dir: ensure => directory, } file { $same_dir: ensure => directory, } PP step "verify we can't create same dir resource with a trailing slash" do options = {:acceptable_exit_codes => [1]} on(agent, puppet_apply("--noop #{dir_manifest}"), options) do |result| unless agent['locale'] == 'ja' assert_match('Cannot alias File', result.output, 'duplicate directory resources did not fail properly') end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/should_create_empty.rb000066400000000000000000000010601470131746300300560ustar00rootroot00000000000000test_name "should create empty file for 'present'" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| target = agent.tmpfile("empty") step "clean up the system before we begin" on(agent, "rm -rf #{target}") step "verify we can create an empty file" on(agent, puppet_resource("file", target, 'ensure=present')) step "verify the target was created" on(agent, "test -f #{target} && test ! -s #{target}") step "clean up after the test run" on(agent, "rm -rf #{target}") end puppetlabs-puppet-789f600/acceptance/tests/resource/file/should_create_symlink.rb000066400000000000000000000047531470131746300304220ustar00rootroot00000000000000test_name "should create symlink" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' def message 'hello world' end def reset_link_and_target(agent, link, target) step "clean up the system before we begin" on agent, "rm -rf #{target} #{link}" on agent, "echo '#{message}' > #{target}" end def verify_symlink(agent, link, target) step "verify the symlink was created" on(agent, "test -L #{link} && test -f #{link}") step "verify the symlink points to a file" on(agent, "test -f #{target}") step "verify the content is identical on both sides" on(agent, "cat #{link}") do |result| fail_test "link missing content" unless result.stdout.include?(message) end on(agent, "cat #{target}") do |result| fail_test "target missing content" unless result.stdout.include?(message) end end agents.each do |agent| if agent.platform.variant == 'windows' # symlinks are supported only on Vista+ (version 6.0 and higher) on(agent, facter('kernelmajversion')) do |result| skip_test "Test not supported on this platform" if result.stdout.chomp.to_f < 6.0 end end link_file = agent.tmpfile("symlink-link") target_file = agent.tmpfile("symlink-target") link_dir = agent.tmpdir("dir_symlink-link") target_dir = agent.tmpdir("dir-symlink-target") reset_link_and_target(agent, link_file, target_file) reset_link_and_target(agent, link_dir, target_dir) step "verify we can create a symlink with puppet resource" on(agent, puppet_resource("file", "#{link_file}", "ensure=#{target_file}")) verify_symlink(agent, link_file, target_file) reset_link_and_target(agent, link_file, target_file) step "verify that 'links => manage' preserves a symlink" apply_manifest_on(agent, "file { '#{link_file}': ensure => link, target => '#{target_file}', links => manage }") verify_symlink(agent, link_file, target_file) reset_link_and_target(agent, link_file, target_file) step "verify that 'links => manage' and 'recurse => true' preserves links in a directory" on(agent, puppet_resource("file", target_dir, "ensure=directory")) reset_link_and_target(agent, link_dir, "#{target_dir}/symlink-target") apply_manifest_on(agent, "file { '#{link_dir}': ensure => directory, target => '#{target_dir}', links => manage, recurse => true }") verify_symlink(agent, "#{link_dir}/symlink-target", "#{target_dir}/symlink-target") step "clean up after the test run" on agent, "rm -rf #{target_file} #{link_file} #{target_dir} #{link_dir}" end puppetlabs-puppet-789f600/acceptance/tests/resource/file/should_default_mode.rb000066400000000000000000000026621470131746300300360ustar00rootroot00000000000000test_name "file resource: set default modes" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' def regexp_mode(mode) Regexp.new("mode\s*=>\s*'0?#{mode}'") end agents.each do |agent| step "setup" parent = agent.tmpdir('default-mode-parent') on(agent, "rm -rf #{parent}") step "puppet should set execute bit on readable directories" on(agent, puppet_resource("file", parent, "ensure=directory", "mode=0644")) do |result| assert_match(regexp_mode(755), result.stdout) end step "include execute bit on newly created directories" dir = "#{parent}/dir" on(agent, "mkdir #{dir} && cd #{dir} && cd ..") step "exclude execute bit from newly created files" file = "#{parent}/file.txt" on(agent, "echo foobar > #{file}") on(agent, "#{file}", :acceptable_exit_codes => (1..255)) do |result| refute_match(/foobar/, result.stdout) end step "set execute bit on file if explicitly specified" file_750 = "#{parent}/file_750.txt" on(agent, puppet_resource("file", file_750, "ensure=file", "mode=0750")) do |result| assert_match(regexp_mode(750), result.stdout) end step "don't set execute bit if directory not readable" dir_600 = "#{parent}/dir_600" on(agent, puppet_resource("file", dir_600, "ensure=directory", "mode=0600")) do |result| assert_match(regexp_mode(700), result.stdout) # readable by owner, but not group end on(agent, "rm -rf #{parent}") end puppetlabs-puppet-789f600/acceptance/tests/resource/file/should_remove_dir.rb000066400000000000000000000016621470131746300275400ustar00rootroot00000000000000test_name "should remove directory, but force required" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| target = agent.tmpdir("delete-dir") step "clean up the system before we begin" on(agent, "rm -rf #{target} ; mkdir -p #{target}") step "verify we can't remove a directory without 'force'" on(agent, puppet_resource("file", target, 'ensure=absent')) do |result| fail_test "didn't tell us that force was required" unless result.stdout.include? "Not removing directory; use 'force' to override" unless agent['locale'] == 'ja' end step "verify the directory still exists" on(agent, "test -d #{target}") step "verify we can remove a directory with 'force'" on(agent, puppet_resource("file", target, 'ensure=absent', 'force=true')) step "verify that the directory is gone" on(agent, "test -d #{target}", :acceptable_exit_codes => [1]) end puppetlabs-puppet-789f600/acceptance/tests/resource/file/should_remove_file.rb000066400000000000000000000007541470131746300277020ustar00rootroot00000000000000test_name "should remove file" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| target = agent.tmpfile('delete-file') step "clean up the system before we begin" on agent, "rm -rf #{target} && touch #{target}" step "verify we can remove a file" on(agent, puppet_resource("file", target, 'ensure=absent')) step "verify that the file is gone" on agent, "test -e #{target}", :acceptable_exit_codes => [1] end puppetlabs-puppet-789f600/acceptance/tests/resource/file/source_attribute.rb000066400000000000000000000276111470131746300274140ustar00rootroot00000000000000test_name "The source attribute" do require 'puppet/acceptance/module_utils' extend Puppet::Acceptance::ModuleUtils tag 'audit:high', 'audit:acceptance', 'server' @target_file_on_windows = 'C:/windows/temp/source_attr_test' @target_file_on_nix = '/tmp/source_attr_test' @target_dir_on_windows = 'C:/windows/temp/source_attr_test_dir' @target_dir_on_nix = '/tmp/source_attr_test_dir' # In case any of the hosts happens to be fips enabled we limit to the lowest # common denominator. checksums_fips = [nil, 'sha256', 'sha256lite', 'ctime', 'mtime'] checksums_no_fips = [nil, 'sha256', 'sha256lite', 'md5', 'md5lite', 'ctime', 'mtime'] fips_host_present = hosts.any? { |host| on(host, facter("fips_enabled")).stdout =~ /true/ } if fips_host_present checksums = checksums_fips else checksums = checksums_no_fips end orig_installed_modules = get_installed_modules_for_hosts hosts teardown do rm_installed_modules_from_hosts orig_installed_modules, (get_installed_modules_for_hosts hosts) hosts.each do |host| file_to_rm = host['platform'] =~ /windows/ ? @target_file_on_windows : @target_file_on_nix dir_to_rm = host['platform'] =~ /windows/ ? @target_dir_on_windows : @target_dir_on_nix checksums.each do |checksum_type| on(host, "rm #{file_to_rm}#{checksum_type}", :acceptable_exit_codes => [0,1]) on(host, "rm -r #{dir_to_rm}#{checksum_type}", :acceptable_exit_codes => [0,1]) end end end step "Setup - create environment and test module" # set directories testdir = master.tmpdir('file_source_attr') env_dir = "#{testdir}/environments" prod_dir = "#{env_dir}/production" manifest_dir = "#{prod_dir}/manifests" manifest_file = "#{prod_dir}/manifests/site.pp" module_dir = "#{prod_dir}/modules" test_module_dir = "#{module_dir}/source_test_module" test_module_manifests_dir = "#{test_module_dir}/manifests" test_module_files_dir = "#{test_module_dir}/files" mod_manifest_file = "#{test_module_manifests_dir}/init.pp" mod_source_file = "#{test_module_files_dir}/source_file" mod_source_dir = "#{test_module_files_dir}/source_dir" mod_source_dir_file = "#{mod_source_dir}/source_dir_file" mod_source = ' the content is present' def mod_manifest_entry(checksum_type = nil) checksum = if checksum_type then "checksum => #{checksum_type}," else "" end manifest = <<-EOF $target_file#{checksum_type} = $::kernel ? { \\'windows\\' => \\'#{@target_file_on_windows}#{checksum_type}\\', default => \\'#{@target_file_on_nix}#{checksum_type}\\' } file { $target_file#{checksum_type}: source => \\'puppet:///modules/source_test_module/source_file\\', #{checksum} ensure => present } $target_dir#{checksum_type} = $::kernel ? { \\'windows\\' => \\'#{@target_dir_on_windows}#{checksum_type}\\', default => \\'#{@target_dir_on_nix}#{checksum_type}\\' } file { $target_dir#{checksum_type}: source => \\'puppet:///modules/source_test_module/source_dir\\', #{checksum} ensure => directory, recurse => true } EOF manifest end mod_manifest = <<-EOF class source_test_module { #{checksums.collect { |checksum_type| mod_manifest_entry(checksum_type) }.join("\n")} } EOF env_manifest = <<-EOF filebucket { \\'main\\': server => \\'#{master}\\', path => false, } File { backup => \\'main\\' } node default { include source_test_module } EOF # apply manifests to setup environment and modules apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) File { ensure => directory, mode => '0755', } file { '#{testdir}':; '#{env_dir}':; '#{prod_dir}':; '#{manifest_dir}':; '#{module_dir}':; '#{test_module_dir}':; '#{test_module_manifests_dir}':; '#{test_module_files_dir}':; } file { '#{mod_manifest_file}': ensure => file, mode => '0644', content => '#{mod_manifest}', } file { '#{mod_source_file}': ensure => file, mode => '0644', content => '#{mod_source}', } file { '#{mod_source_dir}': ensure => directory, mode => '0755' } file { '#{mod_source_dir_file}': ensure => file, mode => '0644', content => '#{mod_source}', } file { '#{manifest_file}': ensure => file, mode => '0644', content => '#{env_manifest}', } MANIFEST step "When using a puppet:/// URI with a master/agent setup" master_opts = { 'main' => { 'environmentpath' => "#{env_dir}", }, } with_puppet_running_on(master, master_opts, testdir) do agents.each do |agent| # accept an exit code of 2 which is returned if there are changes step "create file the first run" on(agent, puppet('agent', "--test"), :acceptable_exit_codes => [0,2]) do file_to_check = agent['platform'] =~ /windows/ ? @target_file_on_windows : @target_file_on_nix dir_to_check = agent['platform'] =~ /windows/ ? @target_dir_on_windows : @target_dir_on_nix checksums.each do |checksum_type| on agent, "cat #{file_to_check}#{checksum_type}" do |result| assert_match(/the content is present/, result.stdout, "Result file not created #{checksum_type}") end on agent, "cat #{dir_to_check}#{checksum_type}/source_dir_file" do |result| assert_match(/the content is present/, result.stdout, "Result file not created #{checksum_type}") end end end step "second run should not update file" on(agent, puppet('agent', "--test"), :acceptable_exit_codes => [0,2]) do |result| refute_match(/content changed.*(md5|sha256)/, result.stdout, "Shouldn't have overwritten any files") # When using ctime/mtime, the agent compares the values from its # local file with the values on the master to determine if the # file is insync or not. If during the first run, the agent # creates the files, and the resulting ctime/mtime are still # behind the times on the master, then the 2nd agent run will # consider the file to not be insync, and will update it # again. This process will repeat until the agent updates the # file, and the resulting ctime/mtime are after the values on # the master, at which point it will have converged. if result.stdout =~ /content changed.*ctime/ Log.warn "Agent did not converge using ctime" end if result.stdout =~ /content changed.*mtime/ Log.warn "Agent did not converge using mtime" end end end =begin # Disable flaky test until PUP-4115 is addressed. step "touch files and verify they're updated with ctime/mtime" # wait until we're not at the mtime of files on the agents # this could be done cross-platform using Puppet, but a single puppet query is unlikely to be less than a second, # and iterating over all agents would be much slower sleep(1) on master, "touch #{mod_source_file} #{mod_source_dir_file}" agents.each do |agent| on(agent, puppet('agent', "--test"), :acceptable_exit_codes => [0,2]) do file_to_check = agent['platform'] =~ /windows/ ? @target_file_on_windows : @target_file_on_nix dir_to_check = agent['platform'] =~ /windows/ ? @target_dir_on_windows : @target_dir_on_nix ['ctime', 'mtime'].each do |time_type| assert_match(/File\[#{file_to_check}#{time_type}\]\/content: content changed/, stdout, "Should have updated files") assert_match(/File\[#{dir_to_check}#{time_type}\/source_dir_file\]\/content: content changed/, stdout, "Should have updated files") end end end =end end # TODO: Add tests for puppet:// URIs with multi-master/agent setups. step "When using puppet apply" agents.each do |agent| step "Setup testing local file sources" # create one larger manifest with all the files so we don't have to run # puppet apply per each checksum_type localsource_testdir = agent.tmpdir('local_source_file_test') source = "#{localsource_testdir}/source_mod/files/source" on agent, "mkdir -p #{File.dirname(source)}" # don't put a 'z' in this content source_content = 'Yay, this is the local file. I have to be bigger than 512 bytes so that my masters. yadda yadda yadda not a nice thing. lorem ipsem. alice bob went to fetch a pail of water. Lorem ipsum dolor sit amet, pede ipsum nam wisi lectus eget, sociis sed, commodo vitae velit eleifend. Vestibulum orci feugiat erat etiam pellentesque sed, imperdiet a integer nulla, mi tincidunt suscipit. Nec sed, mi tortor, in a consequat mattis proin scelerisque eleifend. In lectus magna quam. Magna quam vitae sociosqu. Adipiscing laoreet.' create_remote_file agent, source, source_content local_apply_manifest = "" target = {} checksums.each do |checksum_type| target[checksum_type] = "#{localsource_testdir}/target#{checksum_type}" checksum = if checksum_type then "checksum => #{checksum_type}," else "" end local_apply_manifest.concat("file { '#{target[checksum_type]}': source => '#{source}', ensure => present, #{checksum} }\n") end apply_manifest_on agent, local_apply_manifest checksums.each do |checksum_type| step "Using a local file path. #{checksum_type}" on(agent, "cat #{target[checksum_type]}") do |result| assert_match(/Yay, this is the local file./, result.stdout, "FIRST: File contents not matched on #{agent}") end end step "second run should not update any files" apply_manifest_on(agent, local_apply_manifest) do |result| refute_match(/content changed/, result.stdout, "Shouldn't have overwrote any files") end # changes in source file producing updates is tested elsewhere step "subsequent run should not update file using lite if only after byte 512 is changed" byte_after_md5lite = 513 source_content[byte_after_md5lite] = 'z' create_remote_file agent, source, source_content if fips_host_present apply_manifest_on(agent, "file { '#{localsource_testdir}/targetsha256lite': source => '#{source}', ensure => present, checksum => sha256lite }") do |result| refute_match(/(content changed|defined content)/, result.stdout, "Shouldn't have overwrote any files") end else apply_manifest_on(agent, "file { '#{localsource_testdir}/targetmd5lite': source => '#{source}', ensure => present, checksum => md5lite } file { '#{localsource_testdir}/targetsha256lite': source => '#{source}', ensure => present, checksum => sha256lite }") do |result| refute_match(/(content changed|defined content)/, result.stdout, "Shouldn't have overwrote any files") end end local_module_manifest = "" checksums.each do |checksum_type| on agent, "rm -rf #{target[checksum_type]}" checksum = if checksum_type then "checksum => #{checksum_type}," else "" end local_module_manifest.concat("file { '#{target[checksum_type]}': source => 'puppet:///modules/source_mod/source', ensure => present, #{checksum} }\n") end localsource_test_manifest = agent.tmpfile('local_source_test_manifest') create_remote_file agent, localsource_test_manifest, local_module_manifest on agent, puppet( %{apply --modulepath=#{localsource_testdir} #{localsource_test_manifest}} ) checksums.each do |checksum_type| step "Using a puppet:/// URI with checksum type: #{checksum_type}" on(agent, "cat #{target[checksum_type]}") do |result| assert_match(/Yay, this is the local file./, result.stdout, "FIRST: File contents not matched on #{agent}") end end step "second run should not update any files using apply with puppet:/// URI source" on(agent, puppet( %{apply --modulepath=#{localsource_testdir} #{localsource_test_manifest}} )) do |result| refute_match(/content changed/, result.stdout, "Shouldn't have overwrote any files") end end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/symbolic_modes.rb000066400000000000000000000322321470131746300270340ustar00rootroot00000000000000test_name 'file resource: symbolic modes' do confine :except, :platform => /^windows/ confine :to, {}, hosts.select {|host| !host[:roles].include?('master')} tag 'audit:high', 'audit:acceptance' require 'puppet/acceptance/temp_file_utils' extend Puppet::Acceptance::TempFileUtils class FileSymlink attr_reader :mode, :path, :start_mode, :symbolic_mode def initialize(base_dir, file_type, symbolic_mode, mode, start_mode=nil) @base_dir = base_dir @file_type = file_type @symbolic_mode = symbolic_mode @mode = mode @start_mode = start_mode if @start_mode.nil? @path= "#{@base_dir}/#{@file_type}_#{@symbolic_mode}_#{@mode.to_s(8)}" else @path= "#{@base_dir}/#{@file_type}_#{@symbolic_mode}_#{@start_mode.to_s(8)}_#{@mode.to_s(8)}" end end # does the mode of the file/directory change from start_mode to puppet apply def mode_changes? ! @start_mode.nil? && @start_mode != @mode end def get_manifest "file { #{@path.inspect}: ensure => '#{@file_type}', mode => '#{@symbolic_mode}' }" end end class BaseTest include Beaker::DSL::Assertions def initialize(testcase, agent, base_dir) @testcase = testcase @agent = agent @base_dir = base_dir @file_list = [] @directory_list = [] end def assert_mode(agent, path, expected_mode) permissions = @testcase.stat(agent, path) assert_equal(expected_mode, permissions[2], "'#{path}' current mode #{permissions[2].to_s(8)} doesn't match expected mode #{expected_mode.to_s(8)}") end def manifest manifest_array = (@file_list + @directory_list).map {|x| x.get_manifest} @testcase.step(manifest_array) manifest_array.join("\n") end def puppet_reapply @testcase.apply_manifest_on(@agent, manifest) do |apply_result| refute_match(/mode changed/, apply_result.stdout, "reapplied the symbolic mode change") (@file_list + @directory_list).each do |file| refute_match(/#{Regexp.escape(file.path)}/, apply_result.stdout, "Expected to not see '#{file.path}' in 'puppet apply' output") end end end end class CreateTest < BaseTest def symlink_file(symbolic_mode, mode) @file_list << FileSymlink.new(@base_dir, 'file', symbolic_mode, mode) end def symlink_directory(symbolic_mode, mode) @directory_list << FileSymlink.new(@base_dir, 'directory', symbolic_mode, mode) end def puppet_apply apply_result = @testcase.apply_manifest_on(@agent, manifest).stdout (@file_list + @directory_list).each do |file| assert_match(/File\[#{Regexp.escape(file.path)}\]\/ensure: created/, apply_result, "Failed to create #{file.path}") assert_mode(@agent, file.path, file.mode) end end end class ModifyTest < BaseTest def symlink_file(symbolic_mode, start_mode, mode) @file_list << FileSymlink.new(@base_dir, 'file', symbolic_mode, mode, start_mode) end def symlink_directory(symbolic_mode, start_mode, mode) @directory_list << FileSymlink.new(@base_dir, 'directory', symbolic_mode, mode, start_mode) end def create_starting_state files = @file_list.collect {|x| "'#{x.path}'" } directories = @directory_list.collect {|x| "'#{x.path}'" } @testcase.on(@agent, "touch #{files.join(' ')}") @testcase.on(@agent, "mkdir -p #{directories.join(' ')}") @testcase.on(@agent, "chown symuser:symgroup #{files.join(' ')} #{directories.join(' ')}") cmd_list = [] (@file_list + @directory_list).each do |file| cmd_list << "chmod #{file.start_mode.to_s(8)} '#{file.path}'" end @testcase.on(@agent, cmd_list.join(' && ')) end def puppet_apply @testcase.step(manifest) apply_result = @testcase.apply_manifest_on(@agent, manifest).stdout @testcase.step(apply_result) (@file_list + @directory_list).each do |file| if file.mode_changes? assert_match(/File\[#{Regexp.escape(file.path)}.* mode changed '#{'%04o' % file.start_mode}'.* to '#{'%04o' % file.mode}'/, apply_result, "couldn't set mode to #{file.symbolic_mode}") else refute_match(/#{Regexp.escape(file.path)}.*mode changed/, apply_result, "reapplied the symbolic mode change for file #{file.path}") end assert_mode(@agent, file.path, file.mode) end end end # For your reference: # 4000 the set-user-ID-on-execution bit # 2000 the set-group-ID-on-execution bit # 1000 the sticky bit # 0400 Allow read by owner. # 0200 Allow write by owner. # 0100 For files, allow execution by owner. For directories, allow the # owner to search in the directory. # 0040 Allow read by group members. # 0020 Allow write by group members. # 0010 For files, allow execution by group members. For directories, allow # group members to search in the directory. # 0004 Allow read by others. # 0002 Allow write by others. # 0001 For files, allow execution by others. For directories allow others # to search in the directory. # # On Solaris 11 (from man chmod): # # 20#0 Set group ID on execution if # is 7, 5, 3, or 1. # Enable mandatory locking if # is 6, 4, 2, or 0. # ... # For directories, the set-gid bit can # only be set or cleared by using symbolic mode. # From https://www.gnu.org/software/coreutils/manual/html_node/Symbolic-Modes.html#Symbolic-Modes # Users # u the user who owns the file; # g other users who are in the file's group; # o all other users; # a all users; the same as 'ugo'. # # Operations # + to add the permissions to whatever permissions the users already have for the file; # - to remove the permissions from whatever permissions the users already have for the file; # = to make the permissions the only permissions that the users have for the file. # # Permissions # r the permission the users have to read the file; # w the permission the users have to write to the file; # x the permission the users have to execute the file, or search it if it is a directory. # s the meaning depends on which user (uga) the permission is associated with: # to set set-user-id-on-execution, use 'u' in the users part of the symbolic mode and 's' in the permissions part. # to set set-group-id-on-execution, use 'g' in the users part of the symbolic mode and 's' in the permissions part. # to set both user and group-id-on-execution, omit the users part of the symbolic mode (or use 'a') and use 's' in the permissions part. # t the restricted deletion flag (sticky bit), omit the users part of the symbolic mode (or use 'a') and use 't' in the permissions part. # X execute/search permission is affected only if the file is a directory or already had execute permission. # # Note we do not currently support the Solaris (l) permission: # l mandatory file and record locking refers to a file's ability to have its reading or writing # permissions locked while a program is accessing that file. # agents.each do |agent| is_solaris = agent['platform'].include?('solaris') on(agent, puppet('resource user symuser ensure=present')) on(agent, puppet('resource group symgroup ensure=present')) base_dir_create = agent.tmpdir('symbolic-modes-create_test') base_dir_modify = agent.tmpdir('symbolic-modes-modify_test') teardown do on(agent, puppet('resource user symuser ensure=absent')) on(agent, puppet('resource group symgroup ensure=absent')) on(agent, "rm -rf '#{base_dir_create}' '#{base_dir_modify}'") end create_test = CreateTest.new(self, agent, base_dir_create) create_test.symlink_file('u=r', 00444) create_test.symlink_file('u=w', 00244) create_test.symlink_file('u=x', 00144) create_test.symlink_file('u=rw', 00644) create_test.symlink_file('u=rwx', 00744) create_test.symlink_file('u=rwxt', 01744) create_test.symlink_file('u=rwxs', 04744) create_test.symlink_file('u=rwxts', 05744) create_test.symlink_file('ug=r', 00444) create_test.symlink_file('ug=rw', 00664) create_test.symlink_file('ug=rwx', 00774) create_test.symlink_file('ug=rwxt', 01774) create_test.symlink_file('ug=rwxs', 06774) create_test.symlink_file('ug=rwxts', 07774) create_test.symlink_file('ugo=r', 00444) create_test.symlink_file('ugo=rw', 00666) create_test.symlink_file('ugo=rwx', 00777) create_test.symlink_file('ugo=rwxt', 01777) #create_test.symlink_file('ugo=rwxs', 06777) ## BUG, puppet creates 07777 create_test.symlink_file('ugo=rwxts', 07777) create_test.symlink_file('u=rwx,go=rx', 00755) create_test.symlink_file('u=rwx,g=rx,o=r', 00754) create_test.symlink_file('u=rwx,g=rx,o=', 00750) create_test.symlink_file('a=rwx', 00777) create_test.symlink_file('u+r', 00644) create_test.symlink_file('u+w', 00644) create_test.symlink_file('u+x', 00744) create_test.symlink_directory('u=r', 00455) create_test.symlink_directory('u=w', 00255) create_test.symlink_directory('u=x', 00155) create_test.symlink_directory('u=rw', 00655) create_test.symlink_directory('u=rwx', 00755) create_test.symlink_directory('u=rwxt', 01755) create_test.symlink_directory('u=rwxs', 04755) create_test.symlink_directory('u=rwxts', 05755) create_test.symlink_directory('ug=r', 00445) create_test.symlink_directory('ug=rw', 00665) create_test.symlink_directory('ug=rwx', 00775) create_test.symlink_directory('ug=rwxt', 01775) create_test.symlink_directory('ug=rwxs', 06775) create_test.symlink_directory('ug=rwxts', 07775) create_test.symlink_directory('ugo=r', 00444) create_test.symlink_directory('ugo=rw', 00666) create_test.symlink_directory('ugo=rwx', 00777) create_test.symlink_directory('ugo=rwxt', 01777) #create_test.symlink_directory('ugo=rwxs', 06777) ## BUG, puppet creates 07777 create_test.symlink_directory('ugo=rwxts', 07777) create_test.symlink_directory('u=rwx,go=rx', 00755) create_test.symlink_directory('u=rwx,g=rx,o=r', 00754) create_test.symlink_directory('u=rwx,g=rx,o=', 00750) create_test.symlink_directory('a=rwx', 00777) create_test.symlink_directory('u+r', 00755) create_test.symlink_directory('u+w', 00755) create_test.symlink_directory('u+x', 00755) create_test.puppet_apply() create_test.puppet_reapply() modify_test = ModifyTest.new(self, agent, base_dir_modify) modify_test.symlink_file('u+r', 00200, 00600) modify_test.symlink_file('u+r', 00600, 00600) modify_test.symlink_file('u+w', 00500, 00700) modify_test.symlink_file('u+w', 00400, 00600) modify_test.symlink_file('u+x', 00700, 00700) modify_test.symlink_file('u+x', 00600, 00700) modify_test.symlink_file('u+X', 00100, 00100) modify_test.symlink_file('u+X', 00200, 00200) modify_test.symlink_file('u+X', 00410, 00510) modify_test.symlink_file('a+X', 00600, 00600) modify_test.symlink_file('a+X', 00700, 00711) modify_test.symlink_file('u+s', 00744, 04744) modify_test.symlink_file('g+s', 00744, 02744) modify_test.symlink_file('u+t', 00744, 01744) modify_test.symlink_file('u-r', 00200, 00200) modify_test.symlink_file('u-r', 00600, 00200) modify_test.symlink_file('u-w', 00500, 00500) modify_test.symlink_file('u-w', 00600, 00400) modify_test.symlink_file('u-x', 00700, 00600) modify_test.symlink_file('u-x', 00600, 00600) modify_test.symlink_file('u-s', 04744, 00744) modify_test.symlink_file('g-s', 02744, 00744) modify_test.symlink_file('u-t', 01744, 00744) modify_test.symlink_directory('u+r', 00200, 00600) modify_test.symlink_directory('u+r', 00600, 00600) modify_test.symlink_directory('u+w', 00500, 00700) modify_test.symlink_directory('u+w', 00400, 00600) modify_test.symlink_directory('u+x', 00700, 00700) modify_test.symlink_directory('u+x', 00600, 00700) modify_test.symlink_directory('u+X', 00100, 00100) modify_test.symlink_directory('u+X', 00200, 00300) modify_test.symlink_directory('u+X', 00410, 00510) modify_test.symlink_directory('a+X', 00600, 00711) modify_test.symlink_directory('a+X', 00700, 00711) modify_test.symlink_directory('u+s', 00744, 04744) modify_test.symlink_directory('g+s', 00744, 02744) modify_test.symlink_directory('u+t', 00744, 01744) modify_test.symlink_directory('u-r', 00200, 00200) modify_test.symlink_directory('u-r', 00600, 00200) modify_test.symlink_directory('u-w', 00500, 00500) modify_test.symlink_directory('u-w', 00600, 00400) modify_test.symlink_directory('u-x', 00700, 00600) modify_test.symlink_directory('u-x', 00600, 00600) modify_test.symlink_directory('u-s', 04744, 00744) # using chmod 2744 on a directory to set the start_mode fails on Solaris modify_test.symlink_directory('g-s', 02744, 00744) unless is_solaris modify_test.symlink_directory('u-t', 01744, 00744) modify_test.create_starting_state modify_test.puppet_apply modify_test.puppet_reapply # these raise # test.assert_raises('') # test.assert_raises(' ') # test.assert_raises('u=X') # test.assert_raises('u-X') # test.assert_raises('+l') # test.assert_raises('-l') end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/ticket_6448_file_with_utf8_source.rb000066400000000000000000000066331470131746300323620ustar00rootroot00000000000000test_name 'Ensure a file resource can have a UTF-8 source attribute, content, and path when served via a module' do tag 'audit:high', 'broken:images', 'audit:acceptance', 'server' require 'puppet/acceptance/environment_utils' extend Puppet::Acceptance::EnvironmentUtils require 'puppet/acceptance/agent_fqdn_utils' extend Puppet::Acceptance::AgentFqdnUtils tmp_environment = mk_tmp_environment_with_teardown(master, File.basename(__FILE__, '.*')) agent_tmp_dirs = {} agents.each do |agent| agent_tmp_dirs[agent_to_fqdn(agent)] = agent.tmpdir(tmp_environment) end teardown do # note - master teardown is registered by #mk_tmp_environment_with_teardown step 'remove all test files on agents' do agents.each do |agent| on(agent, "rm -r '#{agent_tmp_dirs[agent_to_fqdn(agent)]}'", :accept_all_exit_codes => true) on(agent, puppet('config print lastrunfile')) do |command_result| agent.rm_rf(command_result.stdout) end end end end step 'create unicode source file served via module on master' do # 静 \u9759 0xE9 0x9D 0x99 http://www.fileformat.info/info/unicode/char/9759/index.htm # 的 \u7684 0xE7 0x9A 0x84 http://www.fileformat.info/info/unicode/char/7684/index.htm # ☃ \2603 0xE2 0x98 0x83 http://www.fileformat.info/info/unicode/char/2603/index.htm setup_module_on_master = <<-MASTER_MANIFEST File { ensure => directory, mode => "0755", } file { '#{environmentpath}/#{tmp_environment}/modules/utf8_file_module':; '#{environmentpath}/#{tmp_environment}/modules/utf8_file_module/files':; } file { '#{environmentpath}/#{tmp_environment}/modules/utf8_file_module/files/\u9759\u7684': ensure => file, content => "\u2603" } MASTER_MANIFEST apply_manifest_on(master, setup_module_on_master, :expect_changes => true) end step 'create a site.pp on master containing a unicode file resource' do site_pp_contents = <<-SITE_PP \$test_path = \$facts['networking']['fqdn'] ? #{agent_tmp_dirs} file { "\$test_path/\uff72\uff67\u30d5\u30eb": ensure => present, source => "puppet:///modules/utf8_file_module/\u9759\u7684", } SITE_PP create_site_pp = <<-CREATE_SITE_PP file { "#{environmentpath}/#{tmp_environment}/manifests/site.pp": ensure => file, content => @(UTF8) #{site_pp_contents} | UTF8 } CREATE_SITE_PP apply_manifest_on(master, create_site_pp, :expect_changes => true) end step 'ensure agent can manage unicode file resource' do # イ \uff72 0xEF 0xBD 0xB2 http://www.fileformat.info/info/unicode/char/ff72/index.htm # ァ \uff67 0xEF 0xBD 0xA7 http://www.fileformat.info/info/unicode/char/ff67/index.htm # フ \u30d5 0xE3 0x83 0x95 http://www.fileformat.info/info/unicode/char/30d5/index.htm # ル \u30eb 0xE3 0x83 0xAB http://www.fileformat.info/info/unicode/char/30eb/index.htm with_puppet_running_on(master, {}) do agents.each do |agent| on(agent, puppet("agent -t --environment '#{tmp_environment}'"), :acceptable_exit_codes => 2) on(agent, "cat '#{agent_tmp_dirs[agent_to_fqdn(agent)]}/\uff72\uff67\u30d5\u30eb'") do |result| assert_match("\u2603", result.stdout, "managed UTF-8 file contents '#{result.stdout}' did not match expected value '\u2603'") end end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/file/ticket_7680-follow-symlinks.rb000066400000000000000000000023441470131746300311430ustar00rootroot00000000000000test_name "#7680: 'links => follow' should use the file source content" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' agents.each do |agent| step "Create file content" real_source = agent.tmpfile('follow_links_source') dest = agent.tmpfile('follow_links_dest') symlink = agent.tmpfile('follow_links_symlink') on agent, "echo 'This is the real content' > #{real_source}" if agent['platform'].include?('windows') # cygwin ln doesn't behave properly, fallback to mklink, # but that requires backslashes, that need to be escaped, # and the link cannot exist prior. on agent, "rm -f #{symlink}" on agent, "cmd /c mklink #{symlink.gsub('/', '\\\\\\\\')} #{real_source.gsub('/', '\\\\\\\\')}" else on agent, "ln -sf #{real_source} #{symlink}" end manifest = <<-MANIFEST file { '#{dest}': ensure => file, source => '#{symlink}', links => follow, } MANIFEST apply_manifest_on(agent, manifest, :trace => true) on(agent, "cat #{dest}") do |result| assert_match(/This is the real content/, result.stdout) end step "Cleanup" [real_source, dest, symlink].each do |file| on agent, "rm -f '#{file}'" end end ticket_8740_should_not_enumerate_root_directory.rb000066400000000000000000000030161470131746300353420ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/filetest_name "#8740: should not enumerate root directory" confine :except, :platform => 'windows' confine :except, :platform => 'osx' tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' target = "/test-socket-#{$$}" require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::CommandUtils agents.each do |agent| step "clean up the system before we begin" on(agent, "rm -f #{target}") step "create UNIX domain socket" on(agent, "#{ruby_command(agent)} -e \"require 'socket'; UNIXServer::new('#{target}').close\"") step "query for all files, which should return nothing" on(agent, puppet_resource('file'), :acceptable_exit_codes => [1]) do |result| assert_match(%r{Listing all file instances is not supported. Please specify a file or directory, e.g. puppet resource file /etc}, result.stderr) end ["/", "/etc"].each do |file| step "query '#{file}' directory, which should return single entry" on(agent, puppet_resource('file', file)) do |result| files = result.stdout.scan(/^file \{ '([^']+)'/).flatten assert_equal(1, files.size, "puppet returned multiple files: #{files.join(', ')}") assert_match(file, files[0], "puppet did not return file") end end step "query file that does not exist, which should report the file is absent" on(agent, puppet_resource('file', '/this/does/notexist')) do |result| assert_match(/ensure\s+=>\s+'absent'/, result.stdout) end step "remove UNIX domain socket" on(agent, "rm -f #{target}") end puppetlabs-puppet-789f600/acceptance/tests/resource/group/000077500000000000000000000000001470131746300237125ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_create.rb000066400000000000000000000012411470131746300270560ustar00rootroot00000000000000test_name "should create a group" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. name = "pl#{rand(999999).to_i}" agents.each do |agent| step "ensure the group does not exist" agent.group_absent(name) step "create the group" on agent, puppet_resource('group', name, 'ensure=present') step "verify the group exists" agent.group_get(name) step "delete the group" agent.group_absent(name) end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_destroy.rb000066400000000000000000000011601470131746300273040ustar00rootroot00000000000000test_name "should destroy a group" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. name = "pl#{rand(999999).to_i}" agents.each do |agent| step "ensure the group is present" agent.group_present(name) step "delete the group" on agent, puppet_resource('group', name, 'ensure=absent') step "verify the group was deleted" agent.group_absent(name) end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_manage_attributes_aix.rb000066400000000000000000000013051470131746300321530ustar00rootroot00000000000000test_name "should correctly manage the attributes property for the Group (AIX only)" do confine :to, :platform => /aix/ tag 'audit:high', 'audit:acceptance' # Could be done as integration tests, but would # require changing the system running the test # in ways that might require special permissions # or be harmful to the system running the test require 'puppet/acceptance/aix_util' extend Puppet::Acceptance::AixUtil initial_attributes = { 'admin' => true } changed_attributes = { 'admin' => false } run_attribute_management_tests('group', :gid, initial_attributes, changed_attributes) end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_manage_members.rb000066400000000000000000000173111470131746300305620ustar00rootroot00000000000000test_name "should correctly manage the members property for the Group resource" do # These are the only platforms whose group providers manage the members # property confine :to, :platform => /windows|osx|aix|^el-|fedora/ tag 'audit:high', 'audit:acceptance' # Could be done as integration tests, but would # require changing the system running the test # in ways that might require special permissions # or be harmful to the system running the test require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::BeakerUtils def random_name "pl#{rand(999999).to_i}" end def group_manifest(user, params) params_str = params.map do |param, value| value_str = value.to_s value_str = "\"#{value_str}\"" if value.is_a?(String) " #{param} => #{value_str}" end.join(",\n") <<-MANIFEST group { '#{user}': #{params_str} } MANIFEST end def members_of(host, group) case host['platform'] when /windows/ # More verbose than 'net localgroup ', but more programmatic # because it does not require us to parse stdout get_group_members = <<-PS1 # Adapted from https://github.com/RamblingCookieMonster/PowerShell/blob/master/Get-ADGroupMembers.ps1 function Get-Members([string] $group) { $ErrorActionPreference = 'Stop' Add-Type -AssemblyName 'System.DirectoryServices.AccountManagement' -ErrorAction Stop $contextType = [System.DirectoryServices.AccountManagement.ContextType]::Machine $groupObject = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity( $contextType, $group ) if (-Not $groupObject) { throw "Could not find the group '$group'!" } $members = $groupObject.GetMembers($false) | ForEach-Object { "'$($_.Name)'" } write-output "[$([string]::join(',', $members))]" } Get-Members #{group} PS1 Kernel.eval( execute_powershell_script_on(host, get_group_members).stdout.chomp ) else # This reads the group members from the /etc/group file get_group_members = <<-RUBY require 'etc' group_struct = nil Etc.group do |g| if g.name == '#{group}' group_struct = g break end end unless group_struct raise "Could not find the group '#{group}'!" end puts(group_struct.mem.to_s) RUBY script_path = "#{host.tmpfile("get_group_members")}.rb" create_remote_file(host, script_path, get_group_members) # The setup step should have already set :privatebindir on the # host. We only include the default here to make this routine # work for local testing, which sometimes skips the setup step. privatebindir = host.has_key?(:privatebindir) ? host[:privatebindir] : '/opt/puppetlabs/puppet/bin' result = on(host, "#{privatebindir}/ruby #{script_path}") Kernel.eval(result.stdout.chomp) end end agents.each do |agent| users = 6.times.collect { random_name } users.each { |user| agent.user_absent(user) } group = random_name agent.group_absent(group) teardown { agent.group_absent(group) } step 'Creating the Users' do users.each do |user| agent.user_present(user) teardown { agent.user_absent(user) } end end group_members = [users[0], users[1]] step 'Ensure that the group is created with the specified members' do manifest = group_manifest(group, members: group_members) apply_manifest_on(agent, manifest) assert_matching_arrays(group_members, members_of(agent, group), "The group was not successfully created with the specified members!") end step "Verify that Puppet errors when one of the members does not exist" do manifest = group_manifest(group, members: ['nonexistent_member']) apply_manifest_on(agent, manifest, :acceptable_exit_codes => [0, 1]) do |result| assert_match(/Error:.*#{group}/, result.stderr, "Puppet fails to report an error when one of the members in the members property does not exist") end end step "Verify that Puppet noops when the group's members are already set after creating the group" do manifest = group_manifest(group, members: group_members) apply_manifest_on(agent, manifest, catch_changes: true) assert_matching_arrays(group_members, members_of(agent, group), "The group's members somehow changed despite Puppet reporting a noop") end step "Verify that Puppet enforces minimum user membership when auth_membership == false" do new_members = [users[2], users[4]] manifest = group_manifest(group, members: new_members, auth_membership: false) apply_manifest_on(agent, manifest) group_members += new_members assert_matching_arrays(group_members, members_of(agent, group), "Puppet fails to enforce minimum user membership when auth_membership == false") end step "Verify that Puppet noops when the group's members are already set after enforcing minimum user membership" do manifest = group_manifest(group, members: group_members) apply_manifest_on(agent, manifest, catch_changes: true) assert_matching_arrays(group_members, members_of(agent, group), "The group's members somehow changed despite Puppet reporting a noop") end # Run some special, platform-specific tests. If these get too large, then # we should consider placing them in a separate file. case agent['platform'] when /windows/ domain = on(agent, 'hostname').stdout.chomp.upcase step "(Windows) Verify that Puppet prints each group member as DOMAIN\\" do new_members = [users[3]] manifest = group_manifest(group, members: new_members, auth_membership: false) apply_manifest_on(agent, manifest) do |result| group_members += new_members stdout = result.stdout.chomp group_members.each do |user| assert_match(/#{domain}\\#{user}/, stdout, "Puppet fails to print the group member #{user} as #{domain}\\#{user}") end end end step "(Windows) Verify that `puppet resource` prints each group member as DOMAIN\\" do on(agent, puppet('resource', 'group', group)) do |result| stdout = result.stdout.chomp group_members.each do |user| assert_match(/#{domain}\\#{user}/, stdout, "`puppet resource` fails to print the group member #{user} as #{domain}\\#{user}") end end end when /aix/ step "(AIX) Verify that Puppet accepts a comma-separated list of members for backwards compatibility" do new_members = [users[3], users[5]] manifest = group_manifest(group, members: new_members.join(','), auth_membership: false) apply_manifest_on(agent, manifest) group_members += new_members assert_matching_arrays(group_members, members_of(agent, group), "Puppet cannot manage the members property when the members are provided as a comma-separated list") end end step "Verify that Puppet enforces inclusive user membership when auth_membership == true" do group_members = [users[0]] manifest = group_manifest(group, members: group_members, auth_membership: true) apply_manifest_on(agent, manifest) assert_matching_arrays(group_members, members_of(agent, group), "Puppet fails to enforce inclusive group membership when auth_membership == true") end step "Verify that Puppet noops when the group's members are already set after enforcing inclusive user membership" do manifest = group_manifest(group, members: group_members) apply_manifest_on(agent, manifest, catch_changes: true) assert_matching_arrays(group_members, members_of(agent, group), "The group's members somehow changed despite Puppet reporting a noop") end end end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_modify_gid.rb000066400000000000000000000024751470131746300277370ustar00rootroot00000000000000test_name "should modify gid of existing group" confine :except, :platform => 'windows' tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. name = "pl#{rand(999999).to_i}" gid1 = (rand(989999).to_i + 10000) gid2 = (rand(989999).to_i + 10000) agents.each do |agent| # AIX group provider returns quoted gids step "ensure that the group exists with gid #{gid1}" on(agent, puppet_resource('group', name, 'ensure=present', "gid=#{gid1}")) do |result| fail_test "missing gid notice" unless result.stdout =~ /gid +=> +'?#{gid1}'?/ end step "ensure that we can modify the GID of the group to #{gid2}" on(agent, puppet_resource('group', name, 'ensure=present', "gid=#{gid2}")) do |result| fail_test "missing gid notice" unless result.stdout =~ /gid +=> +'?#{gid2}'?/ end step "verify that the GID changed" gid_output = agent.group_gid(name).to_i fail_test "gid #{gid_output} does not match expected value of: #{gid2}" unless gid_output == gid2 step "clean up the system after the test run" on(agent, puppet_resource('group', name, 'ensure=absent')) end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_not_create_existing.rb000066400000000000000000000015051470131746300316530ustar00rootroot00000000000000test_name "group should not create existing group" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. name = "gr#{rand(999999).to_i}" agents.each do |agent| step "ensure the group exists on the target node" agent.group_present(name) step "verify that we don't try and create the existing group" on(agent, puppet_resource('group', name, 'ensure=present')) do |result| fail_test "looks like we created the group" if result.stdout.include? "/Group[#{name}]/ensure: created" end step "clean up the system after the test run" agent.group_absent(name) end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_not_destroy_unexisting.rb000066400000000000000000000014061470131746300324440ustar00rootroot00000000000000test_name "should not destroy a group that doesn't exist" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. name = "test-group-#{Time.new.to_i}" step "verify the group does not already exist" agents.each do |agent| agent.group_absent(name) end step "verify that we don't remove the group when it doesn't exist" on(agents, puppet_resource('group', name, 'ensure=absent')) do |result| fail_test "it looks like we tried to remove the group" if result.stdout.include? "/Group[#{name}]/ensure: removed" end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_query.rb000066400000000000000000000016471470131746300267720ustar00rootroot00000000000000test_name "test that we can query and find a group that exists." tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. name = "pl#{rand(999999).to_i}" agents.each do |agent| skip_test('this test fails on windows French due to Cygwin/UTF Issues - PUP-8319,IMAGES-492') if agent['platform'] =~ /windows/ && agent['locale'] == 'fr' step "ensure that our test group exists" agent.group_present(name) step "query for the resource and verify it was found" on(agent, puppet_resource('group', name)) do |result| fail_test "didn't find the group #{name}" unless result.stdout.include? 'present' end step "clean up the group we added" agent.group_absent(name) end puppetlabs-puppet-789f600/acceptance/tests/resource/group/should_query_all.rb000066400000000000000000000015571470131746300276220ustar00rootroot00000000000000test_name "should query all groups" tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:integration' # Does not modify system running test agents.each do |agent| skip_test('this test fails on windows French due to Cygwin/UTF Issues - PUP-8319,IMAGES-492') if agent['platform'] =~ /windows/ && agent['locale'] == 'fr' step "query natively" groups = agent.group_list fail_test("No groups found") unless groups step "query with puppet" on(agent, puppet_resource('group')) do |result| result.stdout.each_line do |line| name = ( line.match(/^group \{ '([^']+)'/) or next )[1] unless groups.delete(name) fail_test "group #{name} found by puppet, not natively" end end end if groups.length > 0 then fail_test "#{groups.length} groups found natively, not puppet: #{groups.join(', ')}" end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/000077500000000000000000000000001470131746300241515ustar00rootroot00000000000000common_package_name_in_different_providers.rb000066400000000000000000000106651470131746300352030ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/packagetest_name "ticket 1073: common package name in two different providers should be allowed" do confine :to, {:platform => /(?:centos|el-|fedora)/}, agents # Skipping tests if facter finds this is an ec2 host, PUP-7774 agents.each do |agent| skip_test('Skipping EC2 Hosts') if fact_on(agent, 'ec2_metadata') end # Upgrade the AlmaLinux release package for newer keys until our image is updated (RE-16096) agents.each do |agent| on(agent, 'dnf -y upgrade almalinux-release') if fact_on(agent, 'os.name') == 'AlmaLinux' end tag 'audit:high', 'audit:acceptance' # Uses a provider that depends on AIO packaging require 'puppet/acceptance/rpm_util' extend Puppet::Acceptance::RpmUtils require 'puppet/acceptance/common_utils' extend Puppet::Acceptance::CommandUtils rpm_options = {:pkg => 'guid', :version => '1.0'} teardown do step "cleanup" agents.each do |agent| clean_rpm agent, rpm_options end end step "Verify gem and ruby-devel on fedora-22 and above if not aio" do if @options[:type] != 'aio' then agents.each do |agent| if agent[:platform] =~ /fedora-2[2-9]/ then unless check_for_package agent, 'rubygems' install_package agent, 'rubygems' end unless check_for_package agent, 'ruby-devel' install_package agent, 'ruby-devel' end end end end end def gem_provider if @options[:type] == 'aio' 'puppet_gem' else 'gem' end end def verify_state(hosts, pkg, state, match) hosts.each do |agent| cmd = rpm_provider(agent) # Note yum lists packages as . on(agent, "#{cmd} list installed") do |result| method(match).call(/^#{pkg}\./, result.stdout) end on(agent, "#{gem_command(agent, @options[:type])} list --local") do |result| method(match).call(/^#{pkg} /, result.stdout) end end end def verify_present(hosts, pkg) verify_state(hosts, pkg, '(?!purged|absent)[^\']+', :assert_match) end def verify_absent(hosts, pkg) verify_state(hosts, pkg, '(?:purged|absent)', :refute_match) end # Setup repo and package agents.each do |agent| clean_rpm agent, rpm_options setup_rpm agent, rpm_options send_rpm agent, rpm_options end verify_absent agents, 'guid' # Test error trying to install duplicate packages collide1_manifest = <<-MANIFEST package {'guid': ensure => installed} package {'other-guid': name => 'guid', ensure => present} MANIFEST apply_manifest_on(agents, collide1_manifest, :acceptable_exit_codes => [1]) do |result| assert_match(/Error while evaluating a Resource Statement, Cannot alias Package\[other-guid\] to \[nil, "guid", nil\]/, "#{result.host}: #{result.stderr}") end verify_absent agents, 'guid' gem_source = if ENV['GEM_SOURCE'] then "source => '#{ENV['GEM_SOURCE']}'," else '' end collide2_manifest = <<-MANIFEST package {'guid': ensure => '0.1.0', provider => #{gem_provider}, #{gem_source}} package {'other-guid': name => 'guid', ensure => installed, provider => #{gem_provider}, #{gem_source}} MANIFEST apply_manifest_on(agents, collide2_manifest, :acceptable_exit_codes => [1]) do |result| assert_match(/Error while evaluating a Resource Statement, Cannot alias Package\[other-guid\] to \[nil, "guid", "#{gem_provider}"\]/, "#{result.host}: #{result.stderr}") end verify_absent agents, 'guid' # Test successful parallel installation install_manifest = <<-MANIFEST package {'guid': ensure => installed} package {'gem-guid': provider => #{gem_provider}, name => 'guid', ensure => installed, #{gem_source} } MANIFEST apply_manifest_on(agents, install_manifest) do |result| assert_match('Package[guid]/ensure: created', "#{result.host}: #{result.stdout}") assert_match('Package[gem-guid]/ensure: created', "#{result.host}: #{result.stdout}") end verify_present agents, 'guid' # Test removal remove_manifest = <<-MANIFEST package {'gem-guid': provider => #{gem_provider}, name => 'guid', ensure => absent, #{gem_source} } package {'guid': ensure => absent} MANIFEST apply_manifest_on(agents, remove_manifest) do |result| assert_match('Package[guid]/ensure: removed', "#{result.host}: #{result.stdout}") assert_match('Package[gem-guid]/ensure: removed', "#{result.host}: #{result.stdout}") end verify_absent agents, 'guid' end puppetlabs-puppet-789f600/acceptance/tests/resource/package/does_not_exist.rb000066400000000000000000000022511470131746300275240ustar00rootroot00000000000000# Redmine (#22529) test_name "Puppet returns only resource package declaration when querying an uninstalled package" do tag 'audit:high', 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. agents.each do |agent| step "test puppet resource package" do on(agent, puppet('resource', 'package', 'not-installed-on-this-host')) do |result| assert_match(/package.*not-installed-on-this-host.*\n.*ensure.*(?:absent|purged).*\n.*provider/, result.stdout) end end end # Until #3707 is fixed and purged rpm/yum packages no longer give spurious creation notices # Also skipping solaris, windows whose providers do not have purgeable implemented. confine_block(:to, :platform => /debian|ubuntu/) do agents.each do |agent| step "test puppet apply" do on(agent, puppet('apply', '-e', %Q|"package {'not-installed-on-this-host': ensure => purged }"|)) do |result| refute_match(/warning/i, result.stdout) end end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/000077500000000000000000000000001470131746300247445ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/basic_tests.rb000066400000000000000000000050431470131746300275760ustar00rootroot00000000000000test_name "Package:IPS basic tests" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent end end agents.each do |agent| step "IPS: clean slate" clean agent step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' set_publisher agent step "IPS: basic ensure we are clean" apply_manifest_on(agent, 'package {mypkg : ensure=>absent}') on(agent, "pkg list -v mypkg", :acceptable_exit_codes => [1]) do refute_match( /mypkg@0.0.1/, result.stdout, "err: #{agent}") end step "IPS: basic - it should create" apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do assert_match( /ensure: created/, result.stdout, "err: #{agent}") end step "IPS: check it was created" on(agent, puppet("resource package mypkg")) do assert_match( /ensure\s+=> '0\.0\.1[,:]?.*'/, result.stdout, "err: #{agent}") end step "IPS: do not upgrade until latest is mentioned" send_pkg agent,:pkg => 'mypkg@0.0.2' apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do refute_match( /ensure: created/, result.stdout, "err: #{agent}") end step "IPS: verify it was not upgraded" on(agent, puppet("resource package mypkg")) do assert_match( /ensure\s+=> '0\.0\.1[,:]?.*'/, result.stdout, "err: #{agent}") end step "IPS: ask to be latest" apply_manifest_on(agent, 'package {mypkg : ensure=>latest}') step "IPS: ensure it was upgraded" on(agent, puppet("resource package mypkg")) do assert_match( /ensure\s+=> '0\.0\.2[,:]?.*'/, result.stdout, "err: #{agent}") end step "IPS: when there are more than one option, choose latest." send_pkg agent,:pkg => 'mypkg@0.0.3' send_pkg agent,:pkg => 'mypkg@0.0.4' apply_manifest_on(agent, 'package {mypkg : ensure=>latest}') on(agent, puppet("resource package mypkg")) do assert_match( /ensure\s+=> '0\.0\.4[,:]?.*'/, result.stdout, "err: #{agent}") end step "IPS: ensure removed." apply_manifest_on(agent, 'package {mypkg : ensure=>absent}') on(agent, "pkg list -v mypkg", :acceptable_exit_codes => [1]) do refute_match( /mypkg@0.0.1/, result.stdout, "err: #{agent}") end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/should_be_holdable.rb000066400000000000000000000060731470131746300310750ustar00rootroot00000000000000test_name "Package:IPS versionable" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent, :pkg => 'mypkg2' clean agent, :pkg => 'mypkg' end end agents.each do |agent| step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' setup_fakeroot2 agent send_pkg2 agent, :pkg => 'mypkg2@0.0.1' set_publisher agent step "IPS: basic - it should create a specific version and install dependent package" apply_manifest_on(agent, 'package {mypkg2 : ensure=>"0.0.1"}') do assert_match( /ensure: created/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.1/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg2" do assert_match( /mypkg2@0.0.1/, result.stdout, "err: #{agent}") end step "IPS: it should upgrade current and dependent package" setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.2' setup_fakeroot2 agent send_pkg2 agent, :pkg => 'mypkg2@0.0.2', :pkgdep => 'mypkg@0.0.2' apply_manifest_on(agent, 'package {mypkg2 : ensure=>"0.0.2"}') do assert_match( /changed/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.2/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg2" do assert_match( /mypkg2@0.0.2/, result.stdout, "err: #{agent}") end step "IPS: it should not upgrade current and dependent package if dependent package is hold" apply_manifest_on(agent, 'package {mypkg : ensure=>"present", mark=>"hold", provider=>"pkg"}') do assert_match( //, result.stdout, "err: #{agent}") end setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.3' setup_fakeroot2 agent send_pkg2 agent, :pkg => 'mypkg2@0.0.3', :pkgdep => 'mypkg@0.0.3' apply_manifest_on(agent, 'package {mypkg2 : ensure=>"0.0.2"}') do refute_match( /changed/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.2/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg2" do assert_match( /mypkg2@0.0.2/, result.stdout, "err: #{agent}") end step "IPS: it should upgrade if hold was released." apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.3", provider=>"pkg"}') do assert_match( //, result.stdout, "err: #{agent}") end apply_manifest_on(agent, 'package {mypkg2 : ensure=>"0.0.3"}') do assert_match( /changed/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.3/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg2" do assert_match( /mypkg2@0.0.3/, result.stdout, "err: #{agent}") end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/should_be_idempotent.rb000066400000000000000000000041731470131746300314720ustar00rootroot00000000000000test_name "Package:IPS idempotency" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent end end agents.each do |agent| step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' set_publisher agent step "IPS: it should create" apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do assert_match( /ensure: created/, result.stdout, "err: #{agent}") end step "IPS: should be idempotent (present)" apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do refute_match( /created/, result.stdout, "err: #{agent}") refute_match( /changed/, result.stdout, "err: #{agent}") end send_pkg agent, :pkg => 'mypkg@0.0.2' step "IPS: ask for latest version" apply_manifest_on(agent, 'package {mypkg : ensure=>latest}') step "IPS: ask for latest version again: should be idempotent (latest)" apply_manifest_on(agent, 'package {mypkg : ensure=>latest}') do refute_match( /created/, result.stdout, "err: #{agent}") end step "IPS: ask for specific version" send_pkg agent,:pkg => 'mypkg@0.0.3' apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.3"}') do assert_match( /changed/, result.stdout, "err: #{agent}") end step "IPS: ask for specific version again: should be idempotent (version)" apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.3"}') do refute_match( /created/, result.stdout, "err: #{agent}") refute_match( /changed/, result.stdout, "err: #{agent}") end step "IPS: ensure removed." apply_manifest_on(agent, 'package {mypkg : ensure=>absent}') on(agent, "pkg list -v mypkg", :acceptable_exit_codes => [1]) do refute_match( /mypkg/, result.stdout, "err: #{agent}") end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/should_be_updatable.rb000066400000000000000000000027251470131746300312640ustar00rootroot00000000000000test_name "Package:IPS test for updatable (update, latest)" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent end end agents.each do |agent| step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' set_publisher agent step "IPS: basic - it should create" apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do assert_match( /ensure: created/, result.stdout, "err: #{agent}") end step "IPS: ask to be latest" send_pkg agent, :pkg => 'mypkg@0.0.2' apply_manifest_on(agent, 'package {mypkg : ensure=>latest}') step "IPS: ensure it was upgraded" on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.2/, result.stdout, "err: #{agent}") end step "IPS: when there are more than one option, choose latest." send_pkg agent,:pkg => 'mypkg@0.0.3' send_pkg agent,:pkg => 'mypkg@0.0.4' apply_manifest_on(agent, 'package {mypkg : ensure=>latest}') on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.4/, result.stdout, "err: #{agent}") end end should_be_updateable_and_unholdable_at_same_time.rb000066400000000000000000000020361470131746300370730ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/package/ipstest_name "Package:IPS test for updatable holded package" do confine :to, :platform => 'solaris-11' tag 'audit:high' require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils agents.each do |agent| teardown do clean agent end step "IPS: setup" do setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' set_publisher agent end step "IPS: it should create and hold in same manifest" do apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.1", mark=>hold}') do |result| assert_match( /ensure: created/, result.stdout, "err: #{agent}") end end step "IPS: it should update and unhold in same manifest" do send_pkg agent, :pkg => 'mypkg@0.0.2' apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.2", mark=>"none"}') end step "IPS: ensure it was upgraded" do on agent, "pkg list -v mypkg" do |result| assert_match( /mypkg@0.0.2/, result.stdout, "err: #{agent}") end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/should_be_versionable.rb000066400000000000000000000033251470131746300316310ustar00rootroot00000000000000test_name "Package:IPS versionable" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent end end agents.each do |agent| step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' send_pkg agent, :pkg => 'mypkg@0.0.2' set_publisher agent step "IPS: basic - it should create a specific version" apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.1"}') do assert_match( /ensure: created/, result.stdout, "err: #{agent}") end on agent, "pkg list mypkg" do assert_match( /0.0.1/, result.stdout, "err: #{agent}") end step "IPS: it should upgrade if asked for next version" apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.2"}') do assert_match( /ensure changed/, result.stdout, "err: #{agent}") end on agent, "pkg list mypkg" do refute_match( /0.0.1/, result.stdout, "err: #{agent}") assert_match( /0.0.2/, result.stdout, "err: #{agent}") end step "IPS: it should downpgrade if asked for previous version" apply_manifest_on(agent, 'package {mypkg : ensure=>"0.0.1"}') do assert_match( /ensure changed/, result.stdout, "err: #{agent}") end on agent, "pkg list mypkg" do refute_match( /0.0.2/, result.stdout, "err: #{agent}") assert_match( /0.0.1/, result.stdout, "err: #{agent}") end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/should_create.rb000066400000000000000000000022511470131746300301120ustar00rootroot00000000000000test_name "Package:IPS basic tests" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent end end agents.each do |agent| step "IPS: clean slate" clean agent step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' set_publisher agent step "IPS: basic - it should create" apply_manifest_on(agent, 'package {mypkg : ensure=>present}') do assert_match( /ensure: created/, result.stdout, "err: #{agent}") end step "IPS: check it was created" on(agent, puppet("resource package mypkg")) do assert_match( /ensure\s+=> '0\.0\.1[,:]?.*'/, result.stdout, "err: #{agent}") end on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.1/, result.stdout, "err: #{agent}") end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/should_query.rb000066400000000000000000000021071470131746300300140ustar00rootroot00000000000000test_name "Package:IPS query" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent end end agents.each do |agent| step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' set_publisher agent step "IPS: basic - it should create" apply_manifest_on(agent, 'package {mypkg : ensure=>"present"}') do assert_match( /ensure: created/, result.stdout, "err: #{agent}") end on(agent, puppet("resource package mypkg")) do assert_match( /0.0.1/, result.stdout, "err: #{agent}") end on(agent, puppet("resource package")) do assert_match( /0.0.1/, result.stdout, "err: #{agent}") end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/ips/should_remove.rb000066400000000000000000000020521470131746300301430ustar00rootroot00000000000000test_name "Package:IPS basic tests" confine :to, :platform => 'solaris-11' tag 'audit:medium', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/solaris_util' extend Puppet::Acceptance::IPSUtils teardown do step "cleanup" agents.each do |agent| clean agent end end agents.each do |agent| step "IPS: setup" setup agent setup_fakeroot agent send_pkg agent, :pkg => 'mypkg@0.0.1' set_publisher agent on agent, "pkg install mypkg" on agent, "pkg list -v mypkg" do assert_match( /mypkg@0.0.1/, result.stdout, "err: #{agent}") end step "IPS: ensure removed." apply_manifest_on(agent, 'package {mypkg : ensure=>absent}') on(agent, "pkg list -v mypkg", :acceptable_exit_codes => [1]) do refute_match( /mypkg@0.0.1/, result.stdout, "err: #{agent}") end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/windows.rb000066400000000000000000000053071470131746300261750ustar00rootroot00000000000000test_name "Windows Package Provider" do confine :to, :platform => 'windows' tag 'audit:high', 'audit:acceptance' require 'puppet/acceptance/windows_utils' extend Puppet::Acceptance::WindowsUtils def package_manifest(name, params, installer_source) params_str = params.map do |param, value| value_str = value.to_s value_str = "\"#{value_str}\"" if value.is_a?(String) " #{param} => #{value_str}" end.join(",\n") <<-MANIFEST package { '#{name}': source => '#{installer_source}', #{params_str} } MANIFEST end mock_package = { :name => "MockPackage" } agents.each do |agent| tmpdir = agent.tmpdir("mock_installer") installer_location = create_mock_package(agent, tmpdir, mock_package) step 'Verify that ensure = present installs the package' do apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :present}, installer_location)) assert(package_installed?(agent, mock_package[:name]), 'Package succesfully installed') end step 'Verify that ensure = absent removes the package' do apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :absent}, installer_location)) assert_equal(false, package_installed?(agent, mock_package[:name]), 'Package successfully Uninstalled') end tmpdir = agent.tmpdir("mock_installer") mock_package[:name] = "MockPackageWithFile" mock_package[:install_commands] = 'System.IO.File.ReadAllLines("install.txt");' installer_location = create_mock_package(agent, tmpdir, mock_package) # Since we didn't add the install.txt package the installation should fail with code 1004 step 'Verify that ensure = present fails when an installer fails with a non-zero exit code' do apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :present}, installer_location)) do |result| assert_match(/#{mock_package[:name]}/, result.stderr, 'Windows package provider did not fail when the package install failed') end end step 'Verify that ensure = present installs a package that requires additional resources' do create_remote_file(agent, "#{tmpdir}/install.txt", 'foobar') apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :present}, installer_location)) assert(package_installed?(agent, mock_package[:name]), 'Package succesfully installed') end step 'Verify that ensure = absent removes the package that required additional resources' do apply_manifest_on(agent, package_manifest(mock_package[:name], {ensure: :absent}, installer_location)) assert_equal(false, package_installed?(agent, mock_package[:name]), 'Package successfully Uninstalled') end end end puppetlabs-puppet-789f600/acceptance/tests/resource/package/yum.rb000066400000000000000000000201721470131746300253120ustar00rootroot00000000000000test_name "test the yum package provider" do confine :to, {:platform => /(?:centos|el-|fedora)/}, agents # Skipping tests if facter finds this is an ec2 host, PUP-7774 agents.each do |agent| skip_test('Skipping EC2 Hosts') if fact_on(agent, 'ec2_metadata') end # Upgrade the AlmaLinux release package for newer keys until our image is updated (RE-16096) agents.each do |agent| on(agent, 'dnf -y upgrade almalinux-release') if fact_on(agent, 'os.name') == 'AlmaLinux' end tag 'audit:high', 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. require 'puppet/acceptance/rpm_util' extend Puppet::Acceptance::RpmUtils epoch_rpm_options = {:pkg => 'epoch', :version => '1.1', :epoch => '1'} no_epoch_rpm_options = {:pkg => 'guid', :version => '1.0'} teardown do step "cleanup" agents.each do |agent| clean_rpm agent, epoch_rpm_options clean_rpm agent, no_epoch_rpm_options end end def verify_state(hosts, pkg, state, match) hosts.each do |agent| cmd = rpm_provider(agent) # Note yum and dnf list packages as . on(agent, "#{cmd} list installed") do |result| method(match).call(/^#{pkg}\./, result.stdout) end end end def verify_present(hosts, pkg) verify_state(hosts, pkg, '(?!purged|absent)[^\']+', :assert_match) end def verify_absent(hosts, pkg) verify_state(hosts, pkg, '(?:purged|absent)', :refute_match) end step "Managing a package which does not include an epoch in its version" do step 'Setup repo and package' agents.each do |agent| clean_rpm agent, no_epoch_rpm_options setup_rpm agent, no_epoch_rpm_options send_rpm agent, no_epoch_rpm_options end step 'Installing a known package succeeds' do verify_absent agents, 'guid' apply_manifest_on(agents, 'package {"guid": ensure => installed}') do |result| assert_match('Package[guid]/ensure: created', "#{result.host}: #{result.stdout}") end end step 'Removing a known package succeeds' do verify_present agents, 'guid' apply_manifest_on(agents, 'package {"guid": ensure => absent}') do |result| assert_match('Package[guid]/ensure: removed', "#{result.host}: #{result.stdout}") end end step 'Installing a specific version of a known package succeeds' do verify_absent agents, 'guid' apply_manifest_on(agents, 'package {"guid": ensure => "1.0"}') do |result| assert_match('Package[guid]/ensure: created', "#{result.host}: #{result.stdout}") end end step 'Removing a specific version of a known package succeeds' do verify_present agents, 'guid' apply_manifest_on(agents, 'package {"guid": ensure => absent}') do |result| assert_match('Package[guid]/ensure: removed', "#{result.host}: #{result.stdout}") end end step 'Installing a non-existent version of a known package fails' do verify_absent agents, 'guid' apply_manifest_on(agents, 'package {"guid": ensure => "1.1"}') do |result| refute_match(/Package\[guid\]\/ensure: created/, "#{result.host}: #{result.stdout}") assert_match("Package[guid]/ensure: change from 'purged' to '1.1' failed", "#{result.host}: #{result.stderr}") end verify_absent agents, 'guid' end step 'Installing a non-existent package fails' do verify_absent agents, 'not_a_package' apply_manifest_on(agents, 'package {"not_a_package": ensure => present}') do |result| refute_match(/Package\[not_a_package\]\/ensure: created/, "#{result.host}: #{result.stdout}") assert_match("Package[not_a_package]/ensure: change from 'purged' to 'present' failed", "#{result.host}: #{result.stderr}") end verify_absent agents, 'not_a_package' end step 'Removing a non-existent package succeeds' do verify_absent agents, 'not_a_package' apply_manifest_on(agents, 'package {"not_a_package": ensure => absent}') do |result| refute_match(/Package\[not_a_package\]\/ensure/, "#{result.host}: #{result.stdout}") assert_match('Applied catalog', "#{result.host}: #{result.stdout}") end verify_absent agents, 'not_a_package' end step 'Installing a known package using source succeeds' do verify_absent agents, 'guid' apply_manifest_on(agents, "package { 'guid': ensure => installed, install_options => '--nogpgcheck', source=>'/tmp/rpmrepo/RPMS/noarch/guid-1.0-1.noarch.rpm' }") do |result| assert_match('Package[guid]/ensure: created', "#{result.host}: #{result.stdout}") end end end ### Epoch tests ### agents.each do |agent| step "Managing a package which includes an epoch in its version" do step "Setup repo and package" do clean_rpm agent, no_epoch_rpm_options setup_rpm agent, epoch_rpm_options send_rpm agent, epoch_rpm_options end step 'Installing a known package with an epoch succeeds' do verify_absent [agent], 'epoch' apply_manifest_on(agent, 'package {"epoch": ensure => installed}') do |result| assert_match('Package[epoch]/ensure: created', "#{result.host}: #{result.stdout}") end end step 'Removing a known package with an epoch succeeds' do verify_present [agent], 'epoch' apply_manifest_on(agent, 'package {"epoch": ensure => absent}') do |result| assert_match('Package[epoch]/ensure: removed', "#{result.host}: #{result.stdout}") end end step "Installing a specific version of a known package with an epoch succeeds when epoch and arch are specified" do verify_absent [agent], 'epoch' apply_manifest_on(agent, "package {'epoch': ensure => '1:1.1-1.noarch'}") do |result| assert_match('Package[epoch]/ensure: created', "#{result.host}: #{result.stdout}") end apply_manifest_on(agent, "package {'epoch': ensure => '1:1.1-1.noarch'}") do |result| refute_match(/epoch/, result.stdout) end end if rpm_provider(agent) == 'dnf' # Yum requires the arch to be specified whenever epoch is specified. This step is only # expected to work in DNF. step "Installing a specific version of a known package with an epoch succeeds when epoch is specified and arch is not" do step "Remove the package" do apply_manifest_on(agent, 'package {"epoch": ensure => absent}') verify_absent [agent], 'epoch' end apply_manifest_on(agent, 'package {"epoch": ensure => "1:1.1-1"}') do |result| assert_match('Package[epoch]/ensure: created', "#{result.host}: #{result.stdout}") end apply_manifest_on(agent, 'package {"epoch": ensure => "1:1.1-1"}') do |result| refute_match(/epoch/, result.stdout) end apply_manifest_on(agent, "package {'epoch': ensure => '1:1.1-1.noarch'}") do |result| refute_match(/epoch/, result.stdout) end end end if rpm_provider(agent) == 'yum' step "Installing a specified version of a known package with an epoch succeeds without epoch or arch provided" do # Due to a bug in DNF, epoch is required. This step is only expected to work in Yum. # See https://bugzilla.redhat.com/show_bug.cgi?id=1286877 step "Remove the package" do apply_manifest_on(agent, 'package {"epoch": ensure => absent}') verify_absent [agent], 'epoch' end apply_manifest_on(agent, 'package {"epoch": ensure => "1.1-1"}') do |result| assert_match('Package[epoch]/ensure: created', "#{result.host}: #{result.stdout}") end apply_manifest_on(agent, 'package {"epoch": ensure => "1.1-1"}') do |result| refute_match(/epoch/, result.stdout) end apply_manifest_on(agent, "package {'epoch': ensure => '1:1.1-1.noarch'}") do |result| refute_match(/epoch/, result.stdout) end end end end end end puppetlabs-puppet-789f600/acceptance/tests/resource/service/000077500000000000000000000000001470131746300242165ustar00rootroot00000000000000puppetlabs-puppet-789f600/acceptance/tests/resource/service/AIX_service_provider.rb000066400000000000000000000064151470131746300306240ustar00rootroot00000000000000test_name 'AIX Service Provider Testing' tag 'audit:high', 'audit:refactor', # Use block style `test_name` 'audit:acceptance' # Could be done at the integration (or unit) layer though # actual changing of resources could irreparably damage a # host running this, or require special permissions. confine :to, :platform => 'aix' require 'puppet/acceptance/service_utils' extend Puppet::Acceptance::ServiceUtils sloth_daemon_script = < } ##################################################################### ### C O N T E X T C O N T E N T T E M P L A T E ##################################################################### CONTEXT_CONTENT = %( ) ##################################################################### ### F O O T E R T E M P L A T E ##################################################################### FOOTER = %( ) ##################################################################### ### F I L E P A G E H E A D E R T E M P L A T E ##################################################################### FILE_PAGE = %{

%short_name%

Path: %full_path% IF:cvsurl  (CVS) ENDIF:cvsurl
Last Update: %dtm_modified%
} ##################################################################### ### C L A S S P A G E H E A D E R T E M P L A T E ##################################################################### CLASS_PAGE = %{
IF:parent ENDIF:parent
%classmod% %full_name%
In: START:infiles IF:full_path_url ENDIF:full_path_url %full_path% IF:full_path_url ENDIF:full_path_url IF:cvsurl  (CVS) ENDIF:cvsurl
END:infiles
Parent: IF:par_url ENDIF:par_url %parent% IF:par_url ENDIF:par_url
} NODE_PAGE = %{
IF:parent ENDIF:parent
%classmod% %full_name%
In: START:infiles IF:full_path_url ENDIF:full_path_url %full_path% IF:full_path_url ENDIF:full_path_url IF:cvsurl  (CVS) ENDIF:cvsurl
END:infiles
Parent: IF:par_url ENDIF:par_url %parent% IF:par_url ENDIF:par_url
} PLUGIN_PAGE = %{
%classmod% %full_name%
In: START:infiles IF:full_path_url ENDIF:full_path_url %full_path% IF:full_path_url ENDIF:full_path_url IF:cvsurl  (CVS) ENDIF:cvsurl
END:infiles
} ##################################################################### ### M E T H O D L I S T T E M P L A T E ##################################################################### PLUGIN_LIST = %(
IF:description
%description%
ENDIF:description IF:toc

Contents

ENDIF:toc
IF:confine START:confine

Confine

%type% %value%
END:confine ENDIF:confine IF:type

Type

%type%
ENDIF:type START:sections
IF:sectitle

%sectitle%

IF:seccomment
%seccomment%
ENDIF:seccomment ENDIF:sectitle END:sections ) METHOD_LIST = %{
IF:diagram
%diagram%
ENDIF:diagram IF:description
%description%
ENDIF:description IF:toc

Contents

ENDIF:toc
IF:childs

Inherited by

START:childs HREF:aref:name: END:childs
ENDIF:childs IF:methods

Defines

START:methods HREF:aref:name:   END:methods
ENDIF:methods IF:resources

Resources

START:resources HREF:aref:name:   END:resources
ENDIF:resources
IF:includes

Included Classes

START:includes HREF:aref:name: END:includes
ENDIF:includes IF:requires

Required Classes

START:requires HREF:aref:name: END:requires
ENDIF:requires IF:realizes

Realized Resources

START:realizes HREF:aref:name: END:realizes
ENDIF:realizes START:sections
IF:sectitle

%sectitle%

IF:seccomment
%seccomment%
ENDIF:seccomment ENDIF:sectitle IF:facts

Custom Facts

START:facts HREF:aref:name:   END:facts
ENDIF:facts IF:plugins

Plugins

START:plugins HREF:aref:name:   END:plugins
ENDIF:plugins IF:nodelist

Nodes

%nodelist%
ENDIF:nodelist IF:classlist

Classes and Modules

%classlist%
ENDIF:classlist IF:constants

Global Variables

START:constants IF:desc ENDIF:desc END:constants
%name% = %value%  %desc%
ENDIF:constants IF:aliases

External Aliases

START:aliases IF:desc ENDIF:desc END:aliases
%old_name% -> %new_name%
  %desc%
ENDIF:aliases IF:attributes

Attributes

START:attributes IF:rw ENDIF:rw IFNOT:rw ENDIF:rw END:attributes
%name% [%rw%]   %a_desc%
ENDIF:attributes IF:method_list
START:method_list IF:methods

Defines

START:methods
IF:m_desc %m_desc% ENDIF:m_desc IF:sourcecode

[Source]

%sourcecode%
ENDIF:sourcecode
END:methods ENDIF:methods END:method_list
ENDIF:method_list IF:resource_list

Resources

START:resource_list
%name%
IF:params START:params    %name% => %value%
END:params ENDIF:params
IF:m_desc %m_desc% ENDIF:m_desc
END:resource_list
ENDIF:resource_list END:sections } ##################################################################### ### B O D Y T E M P L A T E ##################################################################### BODY = HEADER + %( !INCLUDE!
) + METHOD_LIST + %(
) + FOOTER BODYINC = HEADER + %( !INCLUDE!
!INCLUDE!
) + FOOTER ##################################################################### ### S O U R C E C O D E T E M P L A T E ##################################################################### SRC_PAGE = XHTML_PREAMBLE + %( %title%
%code%
) ##################################################################### ### I N D E X F I L E T E M P L A T E S ##################################################################### FR_INDEX_BODY = %( !INCLUDE! ) FILE_INDEX = XHTML_PREAMBLE + %( %list_title%

%list_title%

START:entries %name%
END:entries
) TOP_INDEX = XHTML_PREAMBLE + %{ %list_title%

%list_title%

START:entries %name%
END:entries
} CLASS_INDEX = FILE_INDEX METHOD_INDEX = FILE_INDEX COMBO_INDEX = XHTML_PREAMBLE + %{ %classes_title% & %defines_title%
All Classes

Module

START:module %name%
END:module
IF:nodes

%nodes_title%

START:nodes %name%
END:nodes
ENDIF:nodes IF:classes

%classes_title%

START:classes %name%
END:classes
ENDIF:classes IF:defines

%defines_title%

START:defines %name%
END:defines
ENDIF:defines IF:facts

%facts_title%

START:facts %name%
END:facts
ENDIF:facts IF:plugins

%plugins_title%

START:plugins %name%
END:plugins
ENDIF:plugins
} INDEX = %( %title% ) end # module Page end # class RDoc require 'rdoc/generators/template/html/one_page_html' puppetlabs-puppet-789f600/lib/puppet/util/rdoc/parser.rb000066400000000000000000000010001470131746300232760ustar00rootroot00000000000000# frozen_string_literal: true # Puppet "parser" for the rdoc system # The parser uses puppet parser and traverse the AST to instruct RDoc about # our current structures. It also parses ruby files that could contain # either custom facts or puppet plugins (functions, types...) # rdoc2 includes require 'rdoc/code_objects' require_relative '../../../puppet/util/rdoc/code_objects' require 'rdoc/token_stream' require 'rdoc/markup/pre_process' require 'rdoc/parser' require_relative 'parser/puppet_parser_rdoc2' puppetlabs-puppet-789f600/lib/puppet/util/rdoc/parser/000077500000000000000000000000001470131746300227625ustar00rootroot00000000000000puppetlabs-puppet-789f600/lib/puppet/util/rdoc/parser/puppet_parser_core.rb000066400000000000000000000210331470131746300272070ustar00rootroot00000000000000# frozen_string_literal: true # 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)$/) 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.lookup(:current_environment) scan_top_level(@top_level, environment) @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 |n| prev_container = container container = find_object_named(container, n) container ||= prev_container.add_class(RDoc::PuppetClass, n, 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, environment) # find a module fullpath = File.expand_path(path) Puppet.debug "rdoc: testing #{fullpath}" if fullpath =~ %r{(.*)/([^/]+)/(?:manifests|plugins|lib)/.+\.(rb)$} modpath = ::Regexp.last_match(1) name = ::Regexp.last_match(2) Puppet.debug "rdoc: module #{name} into #{modpath} ?" environment.modulepath.each do |mp| if File.identical?(modpath, mp) Puppet.debug "rdoc: found module #{name}" return name end end end if fullpath =~ /\.(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 environment.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, environment) # 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) # module README should be UTF-8, not default system encoding comment = File.open(readme, "r:UTF-8", &: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, environment) 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. # PUP-3638, keeping this while it should have no effect since no .pp files are now processed 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, @top_level) if @input_file_name =~ /\.rb$/ parse_plugins(mod) end end # create documentation for plugins def parse_plugins(container) Puppet.debug "rdoc: scanning plugin or fact" if @input_file_name =~ %r{/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 case line when /^[ \t]*# ?(.*)$/ comments += ::Regexp.last_match(1) + "\n" when /^[ \t]*(Facter.add|Puppet\.runtime\[:facter\].add)\(['"](.*?)['"]\)/ current_fact = RDoc::Fact.new(::Regexp.last_match(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}" when /^[ \t]*confine[ \t]*:(.*?)[ \t]*=>[ \t]*(.*)$/ current_fact.confine = { :type => ::Regexp.last_match(1), :value => ::Regexp.last_match(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 case line when /^[ \t]*# ?(.*)$/ comments += ::Regexp.last_match(1) + "\n" when /^[ \t]*(?:Puppet::Parser::Functions::)?newfunction[ \t]*\([ \t]*:(.*?)[ \t]*,[ \t]*:type[ \t]*=>[ \t]*(:rvalue|:lvalue)/ current_plugin = RDoc::Plugin.new(::Regexp.last_match(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}" when /^[ \t]*Puppet::Type.newtype[ \t]*\([ \t]*:(.*?)\)/ current_plugin = RDoc::Plugin.new(::Regexp.last_match(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}" when /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 puppetlabs-puppet-789f600/lib/puppet/util/rdoc/parser/puppet_parser_rdoc2.rb000066400000000000000000000005321470131746300272710ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../../puppet/util/rdoc/parser/puppet_parser_core' module RDoc PUPPET_RDOC_VERSION = 2 # @api private class PuppetParserRDoc2 < Parser include PuppetParserCore def create_rdoc_preprocess Markup::PreProcess.new(@input_file_name, @options.rdoc_include) end end end puppetlabs-puppet-789f600/lib/puppet/util/reference.rb000066400000000000000000000036071470131746300230300ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util/instance_loader' require 'fileutils' # Manage Reference Documentation. class Puppet::Util::Reference include Puppet::Util include Puppet::Util::Docs extend Puppet::Util::InstanceLoader instance_load(:reference, 'puppet/reference') def self.modes %w[text] end def self.newreference(name, options = {}, &block) ref = new(name, **options, &block) instance_hash(:reference)[name.intern] = ref ref end def self.page(*sections) depth = 4 # Use the minimum depth sections.each do |name| section = reference(name) or raise _("Could not find section %{name}") % { name: name } depth = section.depth if section.depth < depth end end def self.references(environment) instance_loader(:reference).loadall(environment) loaded_instances(:reference).sort_by(&:to_s) end attr_accessor :page, :depth, :header, :title, :dynamic attr_writer :doc def doc if defined?(@doc) "#{@name} - #{@doc}" else @title end end def dynamic? dynamic end def initialize(name, title: nil, depth: nil, dynamic: nil, doc: nil, &block) @name = name @title = title @depth = depth @dynamic = dynamic @doc = doc meta_def(:generate, &block) # Now handle the defaults @title ||= _("%{name} Reference") % { name: @name.to_s.capitalize } @page ||= @title.gsub(/\s+/, '') @depth ||= 2 @header ||= "" end # Indent every line in the chunk except those which begin with '..'. def indent(text, tab) text.gsub(/(^|\A)/, tab).gsub(/^ +\.\./, "..") end def option(name, value) ":#{name.to_s.capitalize}: #{value}\n" end def text puts output end def to_markdown(withcontents = true) # First the header text = markdown_header(@title, 1) text << @header text << generate text end end puppetlabs-puppet-789f600/lib/puppet/util/resource_template.rb000066400000000000000000000042621470131746300246120ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util' require_relative '../../puppet/util/logging' require 'erb' # A template wrapper that evaluates a template in the # context of a resource, allowing the resource attributes # to be looked up from within the template. # This provides functionality essentially equivalent to # the language's template() function. You pass your file # path and the resource you want to use into the initialization # method, then call result on the instance, and you get back # a chunk of text. # The resource's parameters are available as instance variables # (as opposed to the language, where we use a method_missing trick). # For example, say you have a resource that generates a file. You would # need to implement the following style of `generate` method: # # def generate # template = Puppet::Util::ResourceTemplate.new("/path/to/template", self) # # return Puppet::Type.type(:file).new :path => "/my/file", # :content => template.evaluate # end # # This generated file gets added to the catalog (which is what `generate` does), # and its content is the result of the template. You need to use instance # variables in your template, so if your template just needs to have the name # of the generating resource, it would just have: # # <%= @name %> # # Since the ResourceTemplate class sets as instance variables all of the resource's # parameters. # # Note that this example uses the generating resource as its source of # parameters, which is generally most useful, since it allows you to configure # the generated resource via the generating resource. class Puppet::Util::ResourceTemplate include Puppet::Util::Logging def evaluate set_resource_variables Puppet::Util.create_erb(Puppet::FileSystem.read(@file, :encoding => 'utf-8')).result(binding) end def initialize(file, resource) raise ArgumentError, _("Template %{file} does not exist") % { file: file } unless Puppet::FileSystem.exist?(file) @file = file @resource = resource end private def set_resource_variables @resource.to_hash.each do |param, value| var = "@#{param}" instance_variable_set(var, value) end end end puppetlabs-puppet-789f600/lib/puppet/util/retry_action.rb000066400000000000000000000031411470131746300235650ustar00rootroot00000000000000# frozen_string_literal: true module Puppet::Util::RetryAction class RetryException < Exception; end # rubocop:disable Lint/InheritException class RetryException::NoBlockGiven < RetryException; end class RetryException::NoRetriesGiven < RetryException; end class RetryException::RetriesExceeded < RetryException; end # Execute the supplied block retrying with exponential backoff. # # @param [Hash] options the retry options # @option options [Integer] :retries Maximum number of times to retry. # @option options [Array] :retry_exceptions ([StandardError]) Optional array of exceptions that are allowed to be retried. # @yield The block to be executed. def self.retry_action(options = {}) # Retry actions for a specified amount of time. This method will allow the final # retry to complete even if that extends beyond the timeout period. unless block_given? raise RetryException::NoBlockGiven end retries = options[:retries] if retries.nil? raise RetryException::NoRetriesGiven end retry_exceptions = options[:retry_exceptions] || [StandardError] failures = 0 begin yield rescue *retry_exceptions => e if failures >= retries raise RetryException::RetriesExceeded, _("%{retries} exceeded") % { retries: retries }, e.backtrace end Puppet.info(_("Caught exception %{klass}:%{error} retrying") % { klass: e.class, error: e }) failures += 1 # Increase the amount of time that we sleep after every # failed retry attempt. sleep(((2**failures) - 1) * 0.1) retry end end end puppetlabs-puppet-789f600/lib/puppet/util/rpm_compare.rb000066400000000000000000000144351470131746300233770ustar00rootroot00000000000000# frozen_string_literal: true require 'English' module Puppet::Util::RpmCompare ARCH_LIST = %w[ noarch i386 i686 ppc ppc64 armv3l armv4b armv4l armv4tl armv5tel armv5tejl armv6l armv7l m68kmint s390 s390x ia64 x86_64 sh3 sh4 ].freeze ARCH_REGEX = Regexp.new(ARCH_LIST.map { |arch| "\\.#{arch}" }.join('|')) # This is an attempt at implementing RPM's # lib/rpmvercmp.c rpmvercmp(a, b) in Ruby. # # Some of the things in here look REALLY # UGLY and/or arbitrary. Our goal is to # match how RPM compares versions, quirks # and all. # # I've kept a lot of C-like string processing # in an effort to keep this as identical to RPM # as possible. # # returns 1 if str1 is newer than str2, # 0 if they are identical # -1 if str1 is older than str2 def rpmvercmp(str1, str2) return 0 if str1 == str2 front_strip_re = /^[^A-Za-z0-9~]+/ while str1.length > 0 or str2.length > 0 # trim anything that's in front_strip_re and != '~' off the beginning of each string str1 = str1.gsub(front_strip_re, '') str2 = str2.gsub(front_strip_re, '') # "handle the tilde separator, it sorts before everything else" if str1 =~ /^~/ && str2 =~ /^~/ # if they both have ~, strip it str1 = str1[1..] str2 = str2[1..] next elsif str1 =~ /^~/ return -1 elsif str2 =~ /^~/ return 1 end break if str1.length == 0 or str2.length == 0 # "grab first completely alpha or completely numeric segment" isnum = false # if the first char of str1 is a digit, grab the chunk of continuous digits from each string if str1 =~ /^[0-9]+/ if str1 =~ /^[0-9]+/ segment1 = $LAST_MATCH_INFO.to_s str1 = $LAST_MATCH_INFO.post_match else segment1 = '' end if str2 =~ /^[0-9]+/ segment2 = $LAST_MATCH_INFO.to_s str2 = $LAST_MATCH_INFO.post_match else segment2 = '' end isnum = true # else grab the chunk of continuous alphas from each string (which may be '') else if str1 =~ /^[A-Za-z]+/ segment1 = $LAST_MATCH_INFO.to_s str1 = $LAST_MATCH_INFO.post_match else segment1 = '' end if str2 =~ /^[A-Za-z]+/ segment2 = $LAST_MATCH_INFO.to_s str2 = $LAST_MATCH_INFO.post_match else segment2 = '' end end # if the segments we just grabbed from the strings are different types (i.e. one numeric one alpha), # where alpha also includes ''; "numeric segments are always newer than alpha segments" if segment2.length == 0 return 1 if isnum return -1 end if isnum # "throw away any leading zeros - it's a number, right?" segment1 = segment1.gsub(/^0+/, '') segment2 = segment2.gsub(/^0+/, '') # "whichever number has more digits wins" return 1 if segment1.length > segment2.length return -1 if segment1.length < segment2.length end # "strcmp will return which one is greater - even if the two segments are alpha # or if they are numeric. don't return if they are equal because there might # be more segments to compare" rc = segment1 <=> segment2 return rc if rc != 0 end # end while loop # if we haven't returned anything yet, "whichever version still has characters left over wins" return 1 if str1.length > str2.length return -1 if str1.length < str2.length 0 end # parse a rpm "version" specification # this re-implements rpm's # rpmUtils.miscutils.stringToVersion() in ruby def rpm_parse_evr(full_version) epoch_index = full_version.index(':') if epoch_index epoch = full_version[0, epoch_index] full_version = full_version[epoch_index + 1, full_version.length] else epoch = nil end begin epoch = String(Integer(epoch)) rescue # If there are non-digits in the epoch field, default to nil epoch = nil end release_index = full_version.index('-') if release_index version = full_version[0, release_index] release = full_version[release_index + 1, full_version.length] arch = release.scan(ARCH_REGEX)[0] if arch architecture = arch.delete('.') release.gsub!(ARCH_REGEX, '') end else version = full_version release = nil end { :epoch => epoch, :version => version, :release => release, :arch => architecture } end # this method is a native implementation of the # compare_values function in rpm's python bindings, # found in python/header-py.c, as used by rpm. def compare_values(s1, s2) return 0 if s1.nil? && s2.nil? return 1 if !s1.nil? && s2.nil? return -1 if s1.nil? && !s2.nil? rpmvercmp(s1, s2) end # how rpm compares two package versions: # rpmUtils.miscutils.compareEVR(), which massages data types and then calls # rpm.labelCompare(), found in rpm.git/python/header-py.c, which # sets epoch to 0 if null, then compares epoch, then ver, then rel # using compare_values() and returns the first non-0 result, else 0. # This function combines the logic of compareEVR() and labelCompare(). # # "version_should" can be v, v-r, or e:v-r. # "version_is" will always be at least v-r, can be e:v-r # # return 1: a is newer than b # 0: a and b are the same version # -1: b is newer than a def rpm_compare_evr(should, is) # pass on to rpm labelCompare should_hash = rpm_parse_evr(should) is_hash = rpm_parse_evr(is) unless should_hash[:epoch].nil? rc = compare_values(should_hash[:epoch], is_hash[:epoch]) return rc unless rc == 0 end rc = compare_values(should_hash[:version], is_hash[:version]) return rc unless rc == 0 # here is our special case, PUP-1244. # if should_hash[:release] is nil (not specified by the user), # and comparisons up to here are equal, return equal. We need to # evaluate to whatever level of detail the user specified, so we # don't end up upgrading or *downgrading* when not intended. # # This should NOT be triggered if we're trying to ensure latest. return 0 if should_hash[:release].nil? compare_values(should_hash[:release], is_hash[:release]) end end puppetlabs-puppet-789f600/lib/puppet/util/rubygems.rb000066400000000000000000000033211470131746300227200ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util' module Puppet::Util::RubyGems # Base/factory class for rubygems source. These classes introspec into # rubygems to in order to list where the rubygems system will look for files # to load. class Source class << self # @api private def has_rubygems? # Gems are not actually available when Bundler is loaded, even # though the Gem constant is defined. This is because Bundler # loads in rubygems, but then removes the custom require that # rubygems installs. So when Bundler is around we have to act # as though rubygems is not, e.g. we shouldn't be able to load # a gem that Bundler doesn't want us to see. defined? ::Gem and !defined? ::Bundler end # @api private def source if has_rubygems? Gems18Source else NoGemsSource end end def new(*args) object = source.allocate object.send(:initialize, *args) object end end end # For RubyGems >= 1.8.0 # @api private class Gems18Source < Source def directories # `require 'mygem'` will consider and potentially load # prerelease gems, so we need to match that behavior. # # Just load the stub which points to the gem path, and # delay loading the full specification until if/when the # gem is required. Gem::Specification.stubs.collect do |spec| File.join(spec.full_gem_path, 'lib') end end def clear_paths Gem.clear_paths end end # @api private class NoGemsSource < Source def directories [] end def clear_paths; end end end puppetlabs-puppet-789f600/lib/puppet/util/run_mode.rb000066400000000000000000000075051470131746300227030ustar00rootroot00000000000000# frozen_string_literal: true require 'etc' module Puppet module Util class RunMode def initialize(name) @name = name.to_sym end attr_reader :name def self.[](name) @run_modes ||= {} if Puppet::Util::Platform.windows? @run_modes[name] ||= WindowsRunMode.new(name) else @run_modes[name] ||= UnixRunMode.new(name) end end def server? name == :master || name == :server end def master? name == :master || name == :server end def agent? name == :agent end def user? name == :user end def run_dir RunMode[name].run_dir end def log_dir RunMode[name].log_dir end private ## # select the system or the user directory depending on the context of # this process. The most common use is determining filesystem path # values for confdir and vardir. The intended semantics are: # {https://projects.puppetlabs.com/issues/16637 #16637} for Puppet 3.x # # @todo this code duplicates {Puppet::Settings#which\_configuration\_file} # as described in {https://projects.puppetlabs.com/issues/16637 #16637} def which_dir(system, user) if Puppet.features.root? File.expand_path(system) else File.expand_path(user) end end end class UnixRunMode < RunMode def conf_dir which_dir("/etc/puppetlabs/puppet", "~/.puppetlabs/etc/puppet") end def code_dir which_dir("/etc/puppetlabs/code", "~/.puppetlabs/etc/code") end def var_dir which_dir("/opt/puppetlabs/puppet/cache", "~/.puppetlabs/opt/puppet/cache") end def public_dir which_dir("/opt/puppetlabs/puppet/public", "~/.puppetlabs/opt/puppet/public") end def run_dir which_dir("/var/run/puppetlabs", "~/.puppetlabs/var/run") end def log_dir which_dir("/var/log/puppetlabs/puppet", "~/.puppetlabs/var/log") end def pkg_config_path '/opt/puppetlabs/puppet/lib/pkgconfig' end def gem_cmd '/opt/puppetlabs/puppet/bin/gem' end def common_module_dir '/opt/puppetlabs/puppet/modules' end def vendor_module_dir '/opt/puppetlabs/puppet/vendor_modules' end end class WindowsRunMode < RunMode def conf_dir which_dir(File.join(windows_common_base("puppet/etc")), "~/.puppetlabs/etc/puppet") end def code_dir which_dir(File.join(windows_common_base("code")), "~/.puppetlabs/etc/code") end def var_dir which_dir(File.join(windows_common_base("puppet/cache")), "~/.puppetlabs/opt/puppet/cache") end def public_dir which_dir(File.join(windows_common_base("puppet/public")), "~/.puppetlabs/opt/puppet/public") end def run_dir which_dir(File.join(windows_common_base("puppet/var/run")), "~/.puppetlabs/var/run") end def log_dir which_dir(File.join(windows_common_base("puppet/var/log")), "~/.puppetlabs/var/log") end def pkg_config_path nil end def gem_cmd if (puppet_dir = ENV.fetch('PUPPET_DIR', nil)) File.join(puppet_dir.to_s, 'bin', 'gem.bat') else File.join(Gem.default_bindir, 'gem.bat') end end def common_module_dir "#{installdir}/puppet/modules" if installdir end def vendor_module_dir "#{installdir}\\puppet\\vendor_modules" if installdir end private def installdir ENV.fetch('FACTER_env_windows_installdir', nil) end def windows_common_base(*extra) [ENV.fetch('ALLUSERSPROFILE', nil), "PuppetLabs"] + extra end end end end puppetlabs-puppet-789f600/lib/puppet/util/selinux.rb000066400000000000000000000252711470131746300225620ustar00rootroot00000000000000# frozen_string_literal: true # Provides utility functions to help interface Puppet to SELinux. # # This requires the very new SELinux Ruby bindings. These bindings closely # mirror the SELinux C library interface. # # Support for the command line tools is not provided because the performance # was abysmal. At this time (2008-11-02) the only distribution providing # these Ruby SELinux bindings which I am aware of is Fedora (in libselinux-ruby). Puppet.features.selinux? # check, but continue even if it's not require 'pathname' module Puppet::Util::SELinux S_IFREG = 0o100000 S_IFDIR = 0o040000 S_IFLNK = 0o120000 def self.selinux_support? return false unless defined?(Selinux) if Selinux.is_selinux_enabled == 1 return true end false end def selinux_support? Puppet::Util::SELinux.selinux_support? end # Retrieve and return the full context of the file. If we don't have # SELinux support or if the SELinux call fails then return nil. def get_selinux_current_context(file) return nil unless selinux_support? retval = Selinux.lgetfilecon(file) if retval == -1 return nil end retval[1] end # Retrieve and return the default context of the file. If we don't have # SELinux support or if the SELinux call fails to file a default then return nil. # @deprecated matchpathcon is a deprecated method, selabel_lookup is preferred def get_selinux_default_context(file, resource_ensure = nil) return nil unless selinux_support? # If the filesystem has no support for SELinux labels, return a default of nil # instead of what matchpathcon would return return nil unless selinux_label_support?(file) # If the file exists we should pass the mode to matchpathcon for the most specific # matching. If not, we can pass a mode of 0. mode = file_mode(file, resource_ensure) retval = Selinux.matchpathcon(file, mode) retval == -1 ? nil : retval[1] end # Retrieve and return the default context of the file using an selinux handle. # If we don't have SELinux support or if the SELinux call fails to file a # default then return nil. def get_selinux_default_context_with_handle(file, handle, resource_ensure = nil) return nil unless selinux_support? # If the filesystem has no support for SELinux labels, return a default of nil # instead of what selabel_lookup would return return nil unless selinux_label_support?(file) # Handle is needed for selabel_lookup raise ArgumentError, _("Cannot get default context with nil handle") unless handle # If the file exists we should pass the mode to selabel_lookup for the most specific # matching. If not, we can pass a mode of 0. mode = file_mode(file, resource_ensure) retval = Selinux.selabel_lookup(handle, file, mode) retval == -1 ? nil : retval[1] end # Take the full SELinux context returned from the tools and parse it # out to the three (or four) component parts. Supports :seluser, :selrole, # :seltype, and on systems with range support, :selrange. def parse_selinux_context(component, context) if context.nil? or context == "unlabeled" return nil end components = /^([^\s:]+):([^\s:]+):([^\s:]+)(?::([\sa-zA-Z0-9:,._-]+))?$/.match(context) unless components raise Puppet::Error, _("Invalid context to parse: %{context}") % { context: context } end case component when :seluser components[1] when :selrole components[2] when :seltype components[3] when :selrange components[4] else raise Puppet::Error, _("Invalid SELinux parameter type") end end # This updates the actual SELinux label on the file. You can update # only a single component or update the entire context. # The caveat is that since setting a partial context makes no sense the # file has to already exist. Puppet (via the File resource) will always # just try to set components, even if all values are specified by the manifest. # I believe that the OS should always provide at least a fall-through context # though on any well-running system. def set_selinux_context(file, value, component = false) return nil unless selinux_support? && selinux_label_support?(file) if component # Must first get existing context to replace a single component context = Selinux.lgetfilecon(file)[1] if context == -1 # We can't set partial context components when no context exists # unless/until we can find a way to make Puppet call this method # once for all selinux file label attributes. Puppet.warning _("Can't set SELinux context on file unless the file already has some kind of context") return nil end context = context.split(':') case component when :seluser context[0] = value when :selrole context[1] = value when :seltype context[2] = value when :selrange context[3] = value else raise ArgumentError, _("set_selinux_context component must be one of :seluser, :selrole, :seltype, or :selrange") end context = context.join(':') else context = value end retval = Selinux.lsetfilecon(file, context) if retval == 0 true else Puppet.warning _("Failed to set SELinux context %{context} on %{file}") % { context: context, file: file } false end end # Since this call relies on get_selinux_default_context it also needs a # full non-relative path to the file. Fortunately, that seems to be all # Puppet uses. This will set the file's SELinux context to the policy's # default context (if any) if it differs from the context currently on # the file. def set_selinux_default_context(file, resource_ensure = nil) new_context = get_selinux_default_context(file, resource_ensure) return nil unless new_context cur_context = get_selinux_current_context(file) if new_context != cur_context set_selinux_context(file, new_context) return new_context end nil end ## # selinux_category_to_label is an internal method that converts all # selinux categories to their internal representation, avoiding # potential issues when mcstransd is not functional. # # It is not marked private because it is needed by File's # selcontext.rb, but it is not intended for use outside of Puppet's # code. # # @param category [String] An selinux category, such as "s0" or "SystemLow" # # @return [String] the numeric category name, such as "s0" def selinux_category_to_label(category) # We don't cache this, but there's already a ton of duplicate work # in the selinux handling code. path = Selinux.selinux_translations_path begin File.open(path).each do |line| line.strip! next if line.empty? next if line[0] == "#" # skip comments line.gsub!(/[[:space:]]+/m, '') mapping = line.split("=", 2) if category == mapping[1] return mapping[0] end end rescue SystemCallError => ex log_exception(ex) raise Puppet::Error, _("Could not open SELinux category translation file %{path}.") % { context: context } end category end ######################################################################## # Internal helper methods from here on in, kids. Don't fiddle. private # Check filesystem a path resides on for SELinux support against # whitelist of known-good filesystems. # Returns true if the filesystem can support SELinux labels and # false if not. def selinux_label_support?(file) fstype = find_fs(file) return false if fstype.nil? filesystems = %w[ext2 ext3 ext4 gfs gfs2 xfs jfs btrfs tmpfs zfs] filesystems.include?(fstype) end # Get mode file type bits set based on ensure on # the file resource. This helps SELinux determine # what context a new resource being created should have. def get_create_mode(resource_ensure) mode = 0 case resource_ensure when :present, :file mode |= S_IFREG when :directory mode |= S_IFDIR when :link mode |= S_IFLNK end mode end # If the file/directory/symlink exists, return its mode. Otherwise, get the default mode # that should be used to create the file/directory/symlink taking into account the desired # file type specified in +resource_ensure+. def file_mode(file, resource_ensure) filestat = file_lstat(file) filestat.mode rescue Errno::EACCES 0 rescue Errno::ENOENT if resource_ensure get_create_mode(resource_ensure) else 0 end end # Internal helper function to read and parse /proc/mounts def read_mounts mounts = ''.dup begin if File.method_defined? "read_nonblock" # If possible we use read_nonblock in a loop rather than read to work- # a linux kernel bug. See ticket #1963 for details. mountfh = File.new("/proc/mounts") loop do mounts += mountfh.read_nonblock(1024) end else # Otherwise we shell out and let cat do it for us mountfh = IO.popen("/bin/cat /proc/mounts") mounts = mountfh.read end rescue EOFError # that's expected rescue return nil ensure mountfh.close if mountfh end mntpoint = {} # Read all entries in /proc/mounts. The second column is the # mountpoint and the third column is the filesystem type. # We skip rootfs because it is always mounted at / mounts.each_line do |line| params = line.split(' ') next if params[2] == 'rootfs' mntpoint[params[1]] = params[2] end mntpoint end # Internal helper function to return which type of filesystem a given file # path resides on def find_fs(path) mounts = read_mounts return nil unless mounts # cleanpath eliminates useless parts of the path (like '.', or '..', or # multiple slashes), without touching the filesystem, and without # following symbolic links. This gives the right (logical) tree to follow # while we try and figure out what file-system the target lives on. path = Pathname(path).cleanpath unless path.absolute? raise Puppet::DevError, _("got a relative path in SELinux find_fs: %{path}") % { path: path } end # Now, walk up the tree until we find a match for that path in the hash. path.ascend do |segment| return mounts[segment.to_s] if mounts.has_key?(segment.to_s) end # Should never be reached... mounts['/'] end ## # file_lstat is an internal, private method to allow precise stubbing and # mocking without affecting the rest of the system. # # @return [File::Stat] File.lstat result def file_lstat(path) Puppet::FileSystem.lstat(path) end private :file_lstat end puppetlabs-puppet-789f600/lib/puppet/util/skip_tags.rb000066400000000000000000000004071470131746300230510ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util/tagging' class Puppet::Util::SkipTags include Puppet::Util::Tagging def initialize(stags) self.tags = stags unless defined?(@tags) end def split_qualified_tags? false end end puppetlabs-puppet-789f600/lib/puppet/util/splayer.rb000066400000000000000000000010011470131746300225330ustar00rootroot00000000000000# frozen_string_literal: true # Handle splay options (sleeping for a random interval before executing) module Puppet::Util::Splayer # Have we splayed already? def splayed? !!@splayed end # Sleep when splay is enabled; else just return. def splay(do_splay = Puppet[:splay]) return unless do_splay return if splayed? time = rand(Puppet[:splaylimit] + 1) Puppet.info _("Sleeping for %{time} seconds (splay is enabled)") % { time: time } sleep(time) @splayed = true end end puppetlabs-puppet-789f600/lib/puppet/util/storage.rb000066400000000000000000000052051470131746300225320ustar00rootroot00000000000000# frozen_string_literal: true require 'yaml' require 'singleton' require_relative '../../puppet/util/yaml' # a class for storing state class Puppet::Util::Storage include Singleton include Puppet::Util def self.state @@state end def initialize self.class.load end # Return a hash that will be stored to disk. It's worth noting # here that we use the object's full path, not just the name/type # combination. At the least, this is useful for those non-isomorphic # types like exec, but it also means that if an object changes locations # in the configuration it will lose its cache. def self.cache(object) if object.is_a?(Symbol) name = object else name = object.to_s end @@state[name] ||= {} end def self.clear @@state.clear end def self.init @@state = {} end init def self.load Puppet.settings.use(:main) unless FileTest.directory?(Puppet[:statedir]) filename = Puppet[:statefile] unless Puppet::FileSystem.exist?(filename) init if @@state.nil? return end unless File.file?(filename) Puppet.warning(_("Checksumfile %{filename} is not a file, ignoring") % { filename: filename }) return end Puppet::Util.benchmark(:debug, "Loaded state in %{seconds} seconds") do @@state = Puppet::Util::Yaml.safe_load_file(filename, [Symbol, Time]) rescue Puppet::Util::Yaml::YamlLoadError => detail Puppet.err _("Checksumfile %{filename} is corrupt (%{detail}); replacing") % { filename: filename, detail: detail } begin File.rename(filename, filename + ".bad") rescue raise Puppet::Error, _("Could not rename corrupt %{filename}; remove manually") % { filename: filename }, detail.backtrace end end unless @@state.is_a?(Hash) Puppet.err _("State got corrupted") init end end def self.stateinspect @@state.inspect end def self.store Puppet.debug "Storing state" Puppet.info _("Creating state file %{file}") % { file: Puppet[:statefile] } unless Puppet::FileSystem.exist?(Puppet[:statefile]) if Puppet[:statettl] == 0 || Puppet[:statettl] == Float::INFINITY Puppet.debug "Not pruning old state cache entries" else Puppet::Util.benchmark(:debug, "Pruned old state cache entries in %{seconds} seconds") do ttl_cutoff = Time.now - Puppet[:statettl] @@state.reject! do |k, _v| @@state[k][:checked] && @@state[k][:checked] < ttl_cutoff end end end Puppet::Util.benchmark(:debug, "Stored state in %{seconds} seconds") do Puppet::Util::Yaml.dump(@@state, Puppet[:statefile]) end end end puppetlabs-puppet-789f600/lib/puppet/util/suidmanager.rb000066400000000000000000000126041470131746300233660ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util/warnings' require 'forwardable' require 'etc' module Puppet::Util::SUIDManager include Puppet::Util::Warnings extend Forwardable # Note groups= is handled specially due to a bug in OS X 10.6, 10.7, # and probably upcoming releases... to_delegate_to_process = [:euid=, :euid, :egid=, :egid, :uid=, :uid, :gid=, :gid, :groups] to_delegate_to_process.each do |method| def_delegator Process, method module_function method end def osx_maj_ver return @osx_maj_ver unless @osx_maj_ver.nil? @osx_maj_ver = Puppet.runtime[:facter].value('os.macosx.version.major') || false end module_function :osx_maj_ver def groups=(grouplist) Process.groups = grouplist rescue Errno::EINVAL => e # We catch Errno::EINVAL as some operating systems (OS X in particular) can # cause troubles when using Process#groups= to change *this* user / process # list of supplementary groups membership. This is done via Ruby's function # "static VALUE proc_setgroups(VALUE obj, VALUE ary)" which is effectively # a wrapper for "int setgroups(size_t size, const gid_t *list)" (part of SVr4 # and 4.3BSD but not in POSIX.1-2001) that fails and sets errno to EINVAL. # # This does not appear to be a problem with Ruby but rather an issue on the # operating system side. Therefore we catch the exception and look whether # we run under OS X or not -- if so, then we acknowledge the problem and # re-throw the exception otherwise. if !osx_maj_ver || osx_maj_ver.empty? raise e end end module_function :groups= def self.root? if Puppet::Util::Platform.windows? require_relative '../../puppet/util/windows/user' Puppet::Util::Windows::User.admin? else Process.uid == 0 end end # Methods to handle changing uid/gid of the running process. In general, # these will noop or fail on Windows, and require root to change to anything # but the current uid/gid (which is a noop). # Runs block setting euid and egid if provided then restoring original ids. # If running on Windows or without root, the block will be run with the # current euid/egid. def asuser(new_uid = nil, new_gid = nil) return yield if Puppet::Util::Platform.windows? return yield unless root? return yield unless new_uid or new_gid old_euid = euid old_egid = egid begin change_privileges(new_uid, new_gid, false) yield ensure change_privileges(new_uid ? old_euid : nil, old_egid, false) end end module_function :asuser # If `permanently` is set, will permanently change the uid/gid of the # process. If not, it will only set the euid/egid. If only uid is supplied, # the primary group of the supplied gid will be used. If only gid is # supplied, only gid will be changed. This method will fail if used on # Windows. def change_privileges(uid = nil, gid = nil, permanently = false) return unless uid or gid unless gid uid = convert_xid(:uid, uid) gid = Etc.getpwuid(uid).gid end change_group(gid, permanently) change_user(uid, permanently) if uid end module_function :change_privileges # Changes the egid of the process if `permanently` is not set, otherwise # changes gid. This method will fail if used on Windows, or attempting to # change to a different gid without root. def change_group(group, permanently = false) gid = convert_xid(:gid, group) raise Puppet::Error, _("No such group %{group}") % { group: group } unless gid return if Process.egid == gid if permanently Process::GID.change_privilege(gid) else Process.egid = gid end end module_function :change_group # As change_group, but operates on uids. If changing user permanently, # supplementary groups will be set the to default groups for the new uid. def change_user(user, permanently = false) uid = convert_xid(:uid, user) raise Puppet::Error, _("No such user %{user}") % { user: user } unless uid return if Process.euid == uid if permanently # If changing uid, we must be root. So initgroups first here. initgroups(uid) Process::UID.change_privilege(uid) elsif Process.euid == 0 # We must be root to initgroups, so initgroups before dropping euid if # we're root, otherwise elevate euid before initgroups. # change euid (to root) first. initgroups(uid) Process.euid = uid else Process.euid = uid initgroups(uid) end end module_function :change_user # Make sure the passed argument is a number. def convert_xid(type, id) return id if id.is_a? Integer map = { :gid => :group, :uid => :user } raise ArgumentError, _("Invalid id type %{type}") % { type: type } unless map.include?(type) ret = Puppet::Util.send(type, id) if ret.nil? raise Puppet::Error, _("Invalid %{klass}: %{id}") % { klass: map[type], id: id } end ret end module_function :convert_xid # Initialize primary and supplemental groups to those of the target user. We # take the UID and manually look up their details in the system database, # including username and primary group. This method will fail on Windows, or # if used without root to initgroups of another user. def initgroups(uid) pwent = Etc.getpwuid(uid) Process.initgroups(pwent.name, pwent.gid) end module_function :initgroups end puppetlabs-puppet-789f600/lib/puppet/util/symbolic_file_mode.rb000066400000000000000000000113041470131746300247070ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util' module Puppet module Util module SymbolicFileMode SetUIDBit = ReadBit = 4 SetGIDBit = WriteBit = 2 StickyBit = ExecBit = 1 SymbolicMode = { 'x' => ExecBit, 'w' => WriteBit, 'r' => ReadBit } SymbolicSpecialToBit = { 't' => { 'u' => StickyBit, 'g' => StickyBit, 'o' => StickyBit }, 's' => { 'u' => SetUIDBit, 'g' => SetGIDBit, 'o' => StickyBit } } def valid_symbolic_mode?(value) value = normalize_symbolic_mode(value) return true if value =~ /^0?[0-7]{1,4}$/ return true if value =~ /^([ugoa]*[-=+][-=+rstwxXugo]*)(,[ugoa]*[-=+][-=+rstwxXugo]*)*$/ false end def display_mode(value) if value =~ /^0?[0-7]{1,4}$/ value.rjust(4, "0") else value end end def normalize_symbolic_mode(value) return nil if value.nil? # We need to treat integers as octal numbers. # # "A numeric mode is from one to four octal digits (0-7), derived by adding # up the bits with values 4, 2, and 1. Omitted digits are assumed to be # leading zeros." case value when Numeric value.to_s(8) when /^0?[0-7]{1,4}$/ value.to_i(8).to_s(8) # strip leading 0's else value end end def symbolic_mode_to_int(modification, to_mode = 0, is_a_directory = false) if modification.nil? or modification == '' raise Puppet::Error, _("An empty mode string is illegal") elsif modification =~ /^[0-7]+$/ return modification.to_i(8) elsif modification =~ /^\d+$/ raise Puppet::Error, _("Numeric modes must be in octal, not decimal!") end fail _("non-numeric current mode (%{mode})") % { mode: to_mode.inspect } unless to_mode.is_a?(Numeric) original_mode = { 's' => (to_mode & 0o7000) >> 9, 'u' => (to_mode & 0o0700) >> 6, 'g' => (to_mode & 0o0070) >> 3, 'o' => (to_mode & 0o0007) >> 0, # Are there any execute bits set in the original mode? 'any x?' => (to_mode & 0o0111) != 0 } final_mode = { 's' => original_mode['s'], 'u' => original_mode['u'], 'g' => original_mode['g'], 'o' => original_mode['o'], } modification.split(/\s*,\s*/).each do |part| _, to, dsl = /^([ugoa]*)([-+=].*)$/.match(part).to_a if dsl.nil? then raise Puppet::Error, _('Missing action') end to = "a" unless to and to.length > 0 # We want a snapshot of the mode before we start messing with it to # make actions like 'a-g' atomic. Various parts of the DSL refer to # the original mode, the final mode, or the current snapshot of the # mode, for added fun. snapshot_mode = {} final_mode.each { |k, v| snapshot_mode[k] = v } to.gsub('a', 'ugo').split('').uniq.each do |who| value = snapshot_mode[who] action = '!' actions = { '!' => ->(_, _) { raise Puppet::Error, _('Missing operation (-, =, or +)') }, '=' => ->(m, v) { m | v }, '+' => ->(m, v) { m | v }, '-' => ->(m, v) { m & ~v }, } dsl.split('').each do |op| case op when /[-+=]/ action = op # Clear all bits, if this is assignment value = 0 if op == '=' when /[ugo]/ value = actions[action].call(value, snapshot_mode[op]) when /[rwx]/ value = actions[action].call(value, SymbolicMode[op]) when 'X' # Only meaningful in combination with "set" actions. if action != '+' raise Puppet::Error, _("X only works with the '+' operator") end # As per the BSD manual page, set if this is a directory, or if # any execute bit is set on the original (unmodified) mode. # Ignored otherwise; it is "add if", not "add or clear". if is_a_directory or original_mode['any x?'] value = actions[action].call(value, ExecBit) end when /[st]/ bit = SymbolicSpecialToBit[op][who] or fail _("internal error") final_mode['s'] = actions[action].call(final_mode['s'], bit) else raise Puppet::Error, _('Unknown operation') end end # Now, assign back the value. final_mode[who] = value end rescue Puppet::Error => e if part.inspect != modification.inspect rest = " at #{part.inspect}" else rest = '' end raise Puppet::Error, _("%{error}%{rest} in symbolic mode %{modification}") % { error: e, rest: rest, modification: modification.inspect }, e.backtrace end final_mode['s'] << 9 | final_mode['u'] << 6 | final_mode['g'] << 3 | final_mode['o'] << 0 end end end end puppetlabs-puppet-789f600/lib/puppet/util/tag_set.rb000066400000000000000000000007771470131746300225250ustar00rootroot00000000000000# frozen_string_literal: true require 'set' require_relative '../../puppet/network/format_support' class Puppet::Util::TagSet < Set include Puppet::Network::FormatSupport def self.from_yaml(yaml) new(Puppet::Util::Yaml.safe_load(yaml, [Symbol])) end def to_yaml @hash.keys.to_yaml end def self.from_data_hash(data) new(data) end # TODO: A method named #to_data_hash should not return an array def to_data_hash to_a end def join(*args) to_a.join(*args) end end puppetlabs-puppet-789f600/lib/puppet/util/tagging.rb000066400000000000000000000072061470131746300225110ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util/tag_set' module Puppet::Util::Tagging ValidTagRegex = /\A[[:alnum:]_][[:alnum:]_:.-]*\Z/u # Add a tag to the current tag set. # When a tag set is used for a scope, these tags will be added to all of # the objects contained in this scope when the objects are finished. # def tag(*ary) @tags ||= new_tags ary.flatten.compact.each do |tag| name = tag.to_s.downcase # Add the tag before testing if it's valid since this means that # we never need to test the same valid tag twice. This speeds things # up since we get a lot of duplicates and rarely fail on bad tags if @tags.add?(name) # not seen before, so now we test if it is valid if valid_tag?(name) if split_qualified_tags? # avoid adding twice by first testing if the string contains '::' @tags.merge(name.split('::')) if name.include?('::') end else @tags.delete(name) fail(Puppet::ParseError, _("Invalid tag '%{name}'") % { name: name }) end end end end # Add a name to the current tag set. Silently ignore names that does not # represent valid tags. # # Use this method instead of doing this: # # tag(name) if is_valid?(name) # # since that results in testing the same string twice # def tag_if_valid(name) if name.is_a?(String) && valid_tag?(name) name = name.downcase @tags ||= new_tags if @tags.add?(name) && name.include?('::') @tags.merge(name.split('::')) end end end # Answers if this resource is tagged with at least one of the given tags. # # The given tags are converted to downcased strings before the match is performed. # # @param *tags [String] splat of tags to look for # @return [Boolean] true if this instance is tagged with at least one of the provided tags # def tagged?(*tags) raw_tagged?(tags.collect { |t| t.to_s.downcase }) end # Answers if this resource is tagged with at least one of the tags given in downcased string form. # # The method is a faster variant of the tagged? method that does no conversion of its # arguments. # # @param tag_array [Array[String]] array of tags to look for # @return [Boolean] true if this instance is tagged with at least one of the provided tags # def raw_tagged?(tag_array) my_tags = tags !tag_array.index { |t| my_tags.include?(t) }.nil? end # Only use this method when copying known tags from one Tagging instance to another def set_tags(tag_source) @tags = tag_source.tags end # Return a copy of the tag list, so someone can't ask for our tags # and then modify them. def tags @tags ||= new_tags @tags.dup end # Merge tags from a tagged instance with no attempts to split, downcase # or verify the tags def merge_tags_from(tag_source) @tags ||= new_tags tag_source.merge_into(@tags) end # Merge the tags of this instance into the provide TagSet def merge_into(tag_set) tag_set.merge(@tags) unless @tags.nil? end def tags=(tags) @tags = new_tags return if tags.nil? tags = tags.strip.split(/\s*,\s*/) if tags.is_a?(String) tag(*tags) end def valid_tag?(maybe_tag) tag_enc = maybe_tag.encoding if tag_enc == Encoding::UTF_8 || tag_enc == Encoding::ASCII maybe_tag =~ ValidTagRegex else maybe_tag.encode(Encoding::UTF_8) =~ ValidTagRegex end rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError false end private def split_qualified_tags? true end def new_tags Puppet::Util::TagSet.new end end puppetlabs-puppet-789f600/lib/puppet/util/terminal.rb000066400000000000000000000010571470131746300227020ustar00rootroot00000000000000# frozen_string_literal: true module Puppet::Util::Terminal # Attempts to determine the width of the terminal. This is currently only # supported on POSIX systems, and relies on the claims of `stty` (or `tput`). # # Inspired by code from Thor; thanks wycats! # @return [Number] The column width of the terminal. Defaults to 80 columns. def self.width if Puppet.features.posix? result = %x(stty size 2>/dev/null).split[1] || %x(tput cols 2>/dev/null).split[0] end (result || '80').to_i rescue 80 end end puppetlabs-puppet-789f600/lib/puppet/util/user_attr.rb000066400000000000000000000007451470131746300231020ustar00rootroot00000000000000# frozen_string_literal: true class UserAttr def self.get_attributes_by_name(name) attributes = nil File.readlines('/etc/user_attr').each do |line| next if line =~ /^#/ token = line.split(':') next unless token[0] == name attributes = { :name => name } token[4].split(';').each do |attr| key_value = attr.split('=') attributes[key_value[0].intern] = key_value[1].strip end break end attributes end end puppetlabs-puppet-789f600/lib/puppet/util/warnings.rb000066400000000000000000000013611470131746300227150ustar00rootroot00000000000000# frozen_string_literal: true # Methods to help with handling warnings. module Puppet::Util::Warnings module_function def notice_once(msg) Puppet::Util::Warnings.maybe_log(msg, self.class) { Puppet.notice msg } end def debug_once(msg) return nil unless Puppet[:debug] Puppet::Util::Warnings.maybe_log(msg, self.class) { Puppet.debug msg } end def warnonce(msg) Puppet::Util::Warnings.maybe_log(msg, self.class) { Puppet.warning msg } end def clear_warnings @stampwarnings = {} nil end def self.maybe_log(message, klass) @stampwarnings ||= {} @stampwarnings[klass] ||= [] return nil if @stampwarnings[klass].include? message yield @stampwarnings[klass] << message nil end end puppetlabs-puppet-789f600/lib/puppet/util/watched_file.rb000066400000000000000000000024351470131746300235060ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util/watcher' # Monitor a given file for changes on a periodic interval. Changes are detected # by looking for a change in the file ctime. class Puppet::Util::WatchedFile # @!attribute [r] filename # @return [String] The fully qualified path to the file. attr_reader :filename # @param filename [String] The fully qualified path to the file. # @param timer [Puppet::Util::Watcher::Timer] The polling interval for checking for file # changes. Setting the timeout to a negative value will treat the file as # always changed. Defaults to `Puppet[:filetimeout]` def initialize(filename, timer = Puppet::Util::Watcher::Timer.new(Puppet[:filetimeout])) @filename = filename @timer = timer @info = Puppet::Util::Watcher::PeriodicWatcher.new( Puppet::Util::Watcher::Common.file_ctime_change_watcher(@filename), timer ) end # @return [true, false] If the file has changed since it was last checked. def changed? @info.changed? end # Allow this to be used as the name of the file being watched in various # other methods (such as Puppet::FileSystem.exist?) def to_str @filename end def to_s "" end end puppetlabs-puppet-789f600/lib/puppet/util/watcher.rb000066400000000000000000000006761470131746300225320ustar00rootroot00000000000000# frozen_string_literal: true module Puppet::Util::Watcher require_relative 'watcher/timer' require_relative 'watcher/change_watcher' require_relative 'watcher/periodic_watcher' module Common def self.file_ctime_change_watcher(filename) Puppet::Util::Watcher::ChangeWatcher.watch(lambda do Puppet::FileSystem.stat(filename).ctime rescue Errno::ENOENT, Errno::ENOTDIR :absent end) end end end puppetlabs-puppet-789f600/lib/puppet/util/watcher/000077500000000000000000000000001470131746300221745ustar00rootroot00000000000000puppetlabs-puppet-789f600/lib/puppet/util/watcher/change_watcher.rb000066400000000000000000000014471470131746300254710ustar00rootroot00000000000000# frozen_string_literal: true # Watches for changes over time. It only re-examines the values when it is requested to update readings. # @api private class Puppet::Util::Watcher::ChangeWatcher def self.watch(reader) Puppet::Util::Watcher::ChangeWatcher.new(nil, nil, reader).next_reading end def initialize(previous, current, value_reader) @previous = previous @current = current @value_reader = value_reader end def changed? if uncertain? false else @previous != @current end end def uncertain? @previous.nil? || @current.nil? end def change_current_reading_to(new_value) Puppet::Util::Watcher::ChangeWatcher.new(@current, new_value, @value_reader) end def next_reading change_current_reading_to(@value_reader.call) end end puppetlabs-puppet-789f600/lib/puppet/util/watcher/periodic_watcher.rb000066400000000000000000000017021470131746300260340ustar00rootroot00000000000000# frozen_string_literal: true # Monitor a given watcher for changes on a periodic interval. class Puppet::Util::Watcher::PeriodicWatcher # @param watcher [Puppet::Util::Watcher::ChangeWatcher] a watcher for the value to watch # @param timer [Puppet::Util::Watcher::Timer] A timer to determine when to # recheck the watcher. If the timeout of the timer is negative, then the # watched value is always considered to be changed def initialize(watcher, timer) @watcher = watcher @timer = timer @timer.start end # @return [true, false] If the file has changed since it was last checked. def changed? return true if always_consider_changed? @watcher = examine_watched_info(@watcher) @watcher.changed? end private def always_consider_changed? @timer.timeout < 0 end def examine_watched_info(known) if @timer.expired? @timer.start known.next_reading else known end end end puppetlabs-puppet-789f600/lib/puppet/util/watcher/timer.rb000066400000000000000000000004331470131746300236410ustar00rootroot00000000000000# frozen_string_literal: true class Puppet::Util::Watcher::Timer attr_reader :timeout def initialize(timeout) @timeout = timeout end def start @start_time = now end def expired? (now - @start_time) >= @timeout end def now Time.now.to_i end end puppetlabs-puppet-789f600/lib/puppet/util/windows.rb000066400000000000000000000033511470131746300225600ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/util/platform' module Puppet::Util::Windows module ADSI class ADSIObject; end class User < ADSIObject; end class UserProfile; end class Group < ADSIObject; end end module File; end module Registry end module Service DEFAULT_TIMEOUT = 30 end module SID class Principal; end end class EventLog; end if Puppet::Util::Platform.windows? # Note: Setting codepage here globally ensures all strings returned via # WIN32OLE (Ruby's late-bound COM support) are encoded in Encoding::UTF_8 # # Also, this does not modify the value of WIN32OLE.locale - which defaults # to 2048 (at least on US English Windows) and is not listed in the MS # locales table, here: https://msdn.microsoft.com/en-us/library/ms912047(v=winembedded.10).aspx require 'win32ole'; WIN32OLE.codepage = WIN32OLE::CP_UTF8 # these reference platform specific gems require_relative '../../puppet/ffi/windows' require_relative 'windows/string' require_relative 'windows/error' require_relative 'windows/com' require_relative 'windows/sid' require_relative 'windows/principal' require_relative 'windows/file' require_relative 'windows/security' require_relative 'windows/user' require_relative 'windows/process' require_relative 'windows/root_certs' require_relative 'windows/access_control_entry' require_relative 'windows/access_control_list' require_relative 'windows/security_descriptor' require_relative 'windows/adsi' require_relative 'windows/registry' require_relative 'windows/eventlog' require_relative 'windows/service' require_relative 'windows/monkey_patches/process' end end puppetlabs-puppet-789f600/lib/puppet/util/windows/000077500000000000000000000000001470131746300222315ustar00rootroot00000000000000puppetlabs-puppet-789f600/lib/puppet/util/windows/access_control_entry.rb000066400000000000000000000047241470131746300270070ustar00rootroot00000000000000# frozen_string_literal: true # 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 https://msdn.microsoft.com/en-us/library/windows/desktop/aa374868(v=vs.85).aspx # @api private class Puppet::Util::Windows::AccessControlEntry require_relative '../../../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 directories 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 = ''.dup 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 puppetlabs-puppet-789f600/lib/puppet/util/windows/access_control_list.rb000066400000000000000000000070151470131746300266150ustar00rootroot00000000000000# frozen_string_literal: true # Windows Access Control List # # Represents a list of access control entries (ACEs). # # @see https://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 = Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::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 == Puppet::Util::Windows::SID::LocalSystem end end new_aces << new_ace end @aces = [] if prepend_needed mask = Puppet::Util::Windows::File::STANDARD_RIGHTS_ALL | Puppet::Util::Windows::File::SPECIFIC_RIGHTS_ALL ace = Puppet::Util::Windows::AccessControlEntry.new( Puppet::Util::Windows::SID::LocalSystem, mask ) @aces << ace end @aces.concat(aces_to_prepend) @aces.concat(new_aces) end def inspect str = ''.dup @aces.each do |ace| str << " #{ace.inspect}\n" end str end def ==(other) self.class == other.class && to_a == other.to_a end alias eql? == end puppetlabs-puppet-789f600/lib/puppet/util/windows/adsi.rb000066400000000000000000000521441470131746300235040ustar00rootroot00000000000000# frozen_string_literal: true module Puppet::Util::Windows::ADSI require 'ffi' # https://docs.microsoft.com/en-us/windows/win32/api/dsrole/ne-dsrole-dsrole_machine_role STANDALONE_WORKSTATION = 0 MEMBER_WORKSTATION = 1 STANDALONE_SERVER = 2 MEMBER_SERVER = 3 BACKUP_DOMAIN_CONTROLLER = 4 PRIMARY_DOMAIN_CONTROLLER = 5 DOMAIN_ROLES = { STANDALONE_WORKSTATION => :STANDALONE_WORKSTATION, MEMBER_WORKSTATION => :MEMBER_WORKSTATION, STANDALONE_SERVER => :STANDALONE_SERVER, MEMBER_SERVER => :MEMBER_SERVER, BACKUP_DOMAIN_CONTROLLER => :BACKUP_DOMAIN_CONTROLLER, PRIMARY_DOMAIN_CONTROLLER => :PRIMARY_DOMAIN_CONTROLLER, } class << self extend FFI::Library def connectable?(uri) !!connect(uri) rescue false end def connect(uri) WIN32OLE.connect(uri) rescue WIN32OLERuntimeError => e raise Puppet::Error.new(_("ADSI connection error: %{e}") % { e: e }, e) end def create(name, resource_type) Puppet::Util::Windows::ADSI.connect(computer_uri).Create(resource_type, name) end def delete(name, resource_type) Puppet::Util::Windows::ADSI.connect(computer_uri).Delete(resource_type, name) end # taken from winbase.h MAX_COMPUTERNAME_LENGTH = 31 def computer_name unless @computer_name max_length = MAX_COMPUTERNAME_LENGTH + 1 # NULL terminated FFI::MemoryPointer.new(max_length * 2) do |buffer| # wide string FFI::MemoryPointer.new(:dword, 1) do |buffer_size| buffer_size.write_dword(max_length) # length in TCHARs if GetComputerNameW(buffer, buffer_size) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to get computer name") end @computer_name = buffer.read_wide_string(buffer_size.read_dword) end end end @computer_name end def computer_uri(host = '.') "WinNT://#{host}" end def wmi_resource_uri(host = '.') "winmgmts:{impersonationLevel=impersonate}!//#{host}/root/cimv2" end # This method should *only* be used to generate WinNT:// style monikers # used for IAdsGroup::Add / IAdsGroup::Remove. These URIs are not usable # to resolve an account with WIN32OLE.connect # Valid input is a SID::Principal, S-X-X style SID string or any valid # account name with or without domain prefix # @api private def sid_uri_safe(sid) return sid_uri(sid) if sid.is_a?(Puppet::Util::Windows::SID::Principal) begin sid = Puppet::Util::Windows::SID.name_to_principal(sid) sid_uri(sid) rescue Puppet::Util::Windows::Error, Puppet::Error nil end end # This method should *only* be used to generate WinNT:// style monikers # used for IAdsGroup::Add / IAdsGroup::Remove. These URIs are not useable # to resolve an account with WIN32OLE.connect def sid_uri(sid) raise Puppet::Error, _("Must use a valid SID::Principal") unless sid.is_a?(Puppet::Util::Windows::SID::Principal) "WinNT://#{sid.sid}" end def uri(resource_name, resource_type, host = '.') "#{computer_uri(host)}/#{resource_name},#{resource_type}" end def wmi_connection connect(wmi_resource_uri) end def execquery(query) wmi_connection.execquery(query) end def domain_role unless @domain_role query_result = Puppet::Util::Windows::ADSI.execquery('select DomainRole from Win32_ComputerSystem').to_enum.first @domain_role = DOMAIN_ROLES[query_result.DomainRole] if query_result end @domain_role end ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724295(v=vs.85).aspx # BOOL WINAPI GetComputerName( # _Out_ LPTSTR lpBuffer, # _Inout_ LPDWORD lpnSize # ); ffi_lib :kernel32 attach_function_private :GetComputerNameW, [:lpwstr, :lpdword], :win32_bool end # Common base class shared by the User and Group # classes below. class ADSIObject extend Enumerable # Define some useful class-level methods class << self # Is either 'user' or 'group' attr_reader :object_class def localized_domains @localized_domains ||= [ # localized version of BUILTIN # for instance VORDEFINIERT on German Windows Puppet::Util::Windows::SID.sid_to_name('S-1-5-32').upcase, # localized version of NT AUTHORITY (can't use S-1-5) # for instance AUTORITE NT on French Windows Puppet::Util::Windows::SID.name_to_principal('SYSTEM').domain.upcase ] end def uri(name, host = '.') host = '.' if (localized_domains << Socket.gethostname.upcase).include?(host.upcase) Puppet::Util::Windows::ADSI.uri(name, @object_class, host) end def parse_name(name) if name =~ %r{/} raise Puppet::Error, _("Value must be in DOMAIN\\%{object_class} style syntax") % { object_class: @object_class } end matches = name.scan(/((.*)\\)?(.*)/) domain = matches[0][1] || '.' account = matches[0][2] [account, domain] end # returns Puppet::Util::Windows::SID::Principal[] # may contain objects that represent unresolvable SIDs def get_sids(adsi_child_collection) sids = [] adsi_child_collection.each do |m| sids << Puppet::Util::Windows::SID.ads_to_principal(m) rescue Puppet::Util::Windows::Error => e case e.code when Puppet::Util::Windows::SID::ERROR_TRUSTED_RELATIONSHIP_FAILURE, Puppet::Util::Windows::SID::ERROR_TRUSTED_DOMAIN_FAILURE sids << Puppet::Util::Windows::SID.unresolved_principal(m.name, m.sid) else raise e end end sids end def name_sid_hash(names, allow_unresolved = false) return {} if names.nil? || names.empty? sids = names.map do |name| sid = Puppet::Util::Windows::SID.name_to_principal(name, allow_unresolved) raise Puppet::Error, _("Could not resolve name: %{name}") % { name: name } unless sid [sid.sid, sid] end sids.to_h end def delete(name) Puppet::Util::Windows::ADSI.delete(name, @object_class) end def exists?(name_or_sid) well_known = false if (sid = Puppet::Util::Windows::SID.name_to_principal(name_or_sid)) # Examples of SidType include SidTypeUser, SidTypeGroup if sid.account_type == "SidType#{@object_class.capitalize}".to_sym # Check if we're getting back a local user when domain-joined return true unless [:MEMBER_WORKSTATION, :MEMBER_SERVER].include?(Puppet::Util::Windows::ADSI.domain_role) # The resource domain and the computer name are not always case-matching return sid.domain.casecmp(Puppet::Util::Windows::ADSI.computer_name) == 0 end # 'well known group' is special as it can be a group like Everyone OR a user like SYSTEM # so try to resolve it # https://msdn.microsoft.com/en-us/library/cc234477.aspx well_known = sid.account_type == :SidTypeWellKnownGroup return false if sid.account_type != :SidTypeAlias && !well_known name_or_sid = "#{sid.domain}\\#{sid.account}" end object = Puppet::Util::Windows::ADSI.connect(uri(*parse_name(name_or_sid))) object.Class.downcase == @object_class rescue # special accounts like SYSTEM or special groups like Authenticated Users cannot # resolve via monikers like WinNT://./SYSTEM,user or WinNT://./Authenticated Users,group # -- they'll fail to connect. thus, given a validly resolved SID, this failure is # ambiguous as it may indicate either a group like Service or an account like SYSTEM well_known end def list_all raise NotImplementedError, _("Subclass must implement class-level method 'list_all'!") end def each(&block) objects = [] list_all.each do |o| # Setting WIN32OLE.codepage in the microsoft_windows feature ensures # values are returned as UTF-8 objects << new(o.name) end objects.each(&block) end end attr_reader :name def initialize(name, native_object = nil) @name = name @native_object = native_object end def object_class self.class.object_class end def uri self.class.uri(sid.account, sid.domain) end def native_object @native_object ||= Puppet::Util::Windows::ADSI.connect(self.class.uri(*self.class.parse_name(name))) end def sid @sid ||= Puppet::Util::Windows::SID.octet_string_to_principal(native_object.objectSID) end def [](attribute) # Setting WIN32OLE.codepage ensures values are returned as UTF-8 native_object.Get(attribute) end def []=(attribute, value) native_object.Put(attribute, value) end def commit begin native_object.SetInfo rescue WIN32OLERuntimeError => e # ERROR_BAD_USERNAME 2202L from winerror.h if e.message =~ /8007089A/m raise Puppet::Error, _("Puppet is not able to create/delete domain %{object_class} objects with the %{object_class} resource.") % { object_class: object_class } end raise Puppet::Error.new(_("%{object_class} update failed: %{error}") % { object_class: object_class.capitalize, error: e }, e) end self end end class User < ADSIObject extend FFI::Library require_relative '../../../puppet/util/windows/sid' # https://msdn.microsoft.com/en-us/library/aa746340.aspx # IADsUser interface @object_class = 'user' class << self def list_all Puppet::Util::Windows::ADSI.execquery('select name from win32_useraccount where localaccount = "TRUE"') end def logon(name, password) Puppet::Util::Windows::User.password_is?(name, password) end def create(name) # Windows error 1379: The specified local group already exists. raise Puppet::Error, _("Cannot create user if group '%{name}' exists.") % { name: name } if Puppet::Util::Windows::ADSI::Group.exists? name new(name, Puppet::Util::Windows::ADSI.create(name, @object_class)) end end def password_is?(password) self.class.logon(name, password) end def add_flag(flag_name, value) flag = begin native_object.Get(flag_name) rescue 0 end native_object.Put(flag_name, flag | value) commit end def password=(password) unless password.nil? native_object.SetPassword(password) commit end fADS_UF_DONT_EXPIRE_PASSWD = 0x10000 add_flag("UserFlags", fADS_UF_DONT_EXPIRE_PASSWD) end def groups # https://msdn.microsoft.com/en-us/library/aa746342.aspx # WIN32OLE objects aren't enumerable, so no map groups = [] # Setting WIN32OLE.codepage ensures values are returned as UTF-8 begin native_object.Groups.each { |g| groups << g.Name } rescue nil end groups end def add_to_groups(*group_names) group_names.each do |group_name| Puppet::Util::Windows::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::Windows::ADSI::Group.new(group_name).remove_member_sids(sid) end end alias remove_from_group remove_from_groups def add_group_sids(*sids) group_names = sids.map(&:domain_account) add_to_groups(*group_names) end def remove_group_sids(*sids) group_names = sids.map(&:domain_account) remove_from_groups(*group_names) end def group_sids self.class.get_sids(native_object.Groups) end # TODO: This code's pretty similar to set_members in the Group class. Would be nice # to refactor them into the ADSIObject class at some point. This was not done originally # because these use different methods to do stuff that are also aliased to other methods, # so the shared code isn't exactly a 1:1 mapping. def set_groups(desired_groups, minimum = true) return if desired_groups.nil? desired_groups = desired_groups.split(',').map(&:strip) current_hash = group_sids.to_h { |sid| [sid.sid, sid] } desired_hash = self.class.name_sid_hash(desired_groups) # First we add the user to all the groups it should be in but isn't unless desired_groups.empty? groups_to_add = (desired_hash.keys - current_hash.keys).map { |sid| desired_hash[sid] } add_group_sids(*groups_to_add) end # Then we remove the user from all groups it is in but shouldn't be, if # that's been requested unless minimum if desired_hash.empty? groups_to_remove = current_hash.values else groups_to_remove = (current_hash.keys - desired_hash.keys).map { |sid| current_hash[sid] } end remove_group_sids(*groups_to_remove) end end # Declare all of the available user flags on the system. Note that # ADS_UF is read as ADS_UserFlag # https://docs.microsoft.com/en-us/windows/desktop/api/iads/ne-iads-ads_user_flag # and # https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro # for the flag values. ADS_USERFLAGS = { ADS_UF_SCRIPT: 0x0001, ADS_UF_ACCOUNTDISABLE: 0x0002, ADS_UF_HOMEDIR_REQUIRED: 0x0008, ADS_UF_LOCKOUT: 0x0010, ADS_UF_PASSWD_NOTREQD: 0x0020, ADS_UF_PASSWD_CANT_CHANGE: 0x0040, ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED: 0x0080, ADS_UF_TEMP_DUPLICATE_ACCOUNT: 0x0100, ADS_UF_NORMAL_ACCOUNT: 0x0200, ADS_UF_INTERDOMAIN_TRUST_ACCOUNT: 0x0800, ADS_UF_WORKSTATION_TRUST_ACCOUNT: 0x1000, ADS_UF_SERVER_TRUST_ACCOUNT: 0x2000, ADS_UF_DONT_EXPIRE_PASSWD: 0x10000, ADS_UF_MNS_LOGON_ACCOUNT: 0x20000, ADS_UF_SMARTCARD_REQUIRED: 0x40000, ADS_UF_TRUSTED_FOR_DELEGATION: 0x80000, ADS_UF_NOT_DELEGATED: 0x100000, ADS_UF_USE_DES_KEY_ONLY: 0x200000, ADS_UF_DONT_REQUIRE_PREAUTH: 0x400000, ADS_UF_PASSWORD_EXPIRED: 0x800000, ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION: 0x1000000 } def userflag_set?(flag) flag_value = ADS_USERFLAGS[flag] || 0 !(self['UserFlags'] & flag_value).zero? end # Common helper for set_userflags and unset_userflags. # # @api private def op_userflags(*flags, &block) # Avoid an unnecessary set + commit operation. return if flags.empty? unrecognized_flags = flags.reject { |flag| ADS_USERFLAGS.keys.include?(flag) } unless unrecognized_flags.empty? raise ArgumentError, _("Unrecognized ADS UserFlags: %{unrecognized_flags}") % { unrecognized_flags: unrecognized_flags.join(', ') } end self['UserFlags'] = flags.inject(self['UserFlags'], &block) end def set_userflags(*flags) op_userflags(*flags) { |userflags, flag| userflags | ADS_USERFLAGS[flag] } end def unset_userflags(*flags) op_userflags(*flags) { |userflags, flag| userflags & ~ADS_USERFLAGS[flag] } end def disabled? userflag_set?(:ADS_UF_ACCOUNTDISABLE) end def locked_out? # Note that the LOCKOUT flag is known to be inaccurate when using the # LDAP IADsUser provider, but this class consistently uses the WinNT # provider, which is expected to be accurate. userflag_set?(:ADS_UF_LOCKOUT) end def expired? expires = native_object.Get('AccountExpirationDate') expires && expires < Time.now rescue WIN32OLERuntimeError => e # This OLE error code indicates the property can't be found in the cache raise e unless e.message =~ /8000500D/m false end # UNLEN from lmcons.h - https://stackoverflow.com/a/2155176 MAX_USERNAME_LENGTH = 256 def self.current_user_name user_name = ''.dup max_length = MAX_USERNAME_LENGTH + 1 # NULL terminated FFI::MemoryPointer.new(max_length * 2) do |buffer| # wide string FFI::MemoryPointer.new(:dword, 1) do |buffer_size| buffer_size.write_dword(max_length) # length in TCHARs if GetUserNameW(buffer, buffer_size) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to get user name") end # buffer_size includes trailing NULL user_name = buffer.read_wide_string(buffer_size.read_dword - 1) end end user_name end # https://docs.microsoft.com/en-us/windows/win32/api/secext/ne-secext-extended_name_format NameUnknown = 0 NameFullyQualifiedDN = 1 NameSamCompatible = 2 NameDisplay = 3 NameUniqueId = 6 NameCanonical = 7 NameUserPrincipal = 8 NameCanonicalEx = 9 NameServicePrincipal = 10 NameDnsDomain = 12 NameGivenName = 13 NameSurname = 14 def self.current_user_name_with_format(format) user_name = ''.dup max_length = 1024 FFI::MemoryPointer.new(:lpwstr, max_length * 2 + 1) do |buffer| FFI::MemoryPointer.new(:dword, 1) do |buffer_size| buffer_size.write_dword(max_length + 1) if GetUserNameExW(format.to_i, buffer, buffer_size) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error.new(_("Failed to get user name"), FFI.errno) end user_name = buffer.read_wide_string(buffer_size.read_dword).chomp end end user_name end def self.current_sam_compatible_user_name current_user_name_with_format(NameSamCompatible) end def self.current_user_sid Puppet::Util::Windows::SID.name_to_principal(current_user_name) end ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724432(v=vs.85).aspx # BOOL WINAPI GetUserName( # _Out_ LPTSTR lpBuffer, # _Inout_ LPDWORD lpnSize # ); ffi_lib :advapi32 attach_function_private :GetUserNameW, [:lpwstr, :lpdword], :win32_bool # https://docs.microsoft.com/en-us/windows/win32/api/secext/nf-secext-getusernameexa # BOOLEAN SEC_ENTRY GetUserNameExA( # EXTENDED_NAME_FORMAT NameFormat, # LPSTR lpNameBuffer, # PULONG nSize # );type ffi_lib :secur32 attach_function_private :GetUserNameExW, [:uint16, :lpwstr, :pointer], :win32_bool end class UserProfile def self.delete(sid) Puppet::Util::Windows::ADSI.wmi_connection.Delete("Win32_UserProfile.SID='#{sid}'") rescue WIN32OLERuntimeError => e # https://social.technet.microsoft.com/Forums/en/ITCG/thread/0f190051-ac96-4bf1-a47f-6b864bfacee5 # Prior to Vista SP1, there's no built-in way to programmatically # delete user profiles (except for delprof.exe). So try to delete # but warn if we fail raise e unless e.message.include?('80041010') Puppet.warning _("Cannot delete user profile for '%{sid}' prior to Vista SP1") % { sid: sid } end end class Group < ADSIObject # https://msdn.microsoft.com/en-us/library/aa706021.aspx # IADsGroup interface @object_class = 'group' class << self def list_all Puppet::Util::Windows::ADSI.execquery('select name from win32_group where localaccount = "TRUE"') end def create(name) # Windows error 2224: The account already exists. raise Puppet::Error, _("Cannot create group if user '%{name}' exists.") % { name: name } if Puppet::Util::Windows::ADSI::User.exists?(name) new(name, Puppet::Util::Windows::ADSI.create(name, @object_class)) end end def add_member_sids(*sids) sids.each do |sid| native_object.Add(Puppet::Util::Windows::ADSI.sid_uri(sid)) end end def remove_member_sids(*sids) sids.each do |sid| native_object.Remove(Puppet::Util::Windows::ADSI.sid_uri(sid)) end end # returns Puppet::Util::Windows::SID::Principal[] # may contain objects that represent unresolvable SIDs # qualified account names are returned by calling #domain_account def members self.class.get_sids(native_object.Members) end alias member_sids members def set_members(desired_members, inclusive = true) return if desired_members.nil? current_hash = member_sids.to_h { |sid| [sid.sid, sid] } desired_hash = self.class.name_sid_hash(desired_members) # First we add all missing members unless desired_hash.empty? members_to_add = (desired_hash.keys - current_hash.keys).map { |sid| desired_hash[sid] } add_member_sids(*members_to_add) end # Then we remove all extra members if inclusive if inclusive if desired_hash.empty? members_to_remove = current_hash.values else members_to_remove = (current_hash.keys - desired_hash.keys).map { |sid| current_hash[sid] } end remove_member_sids(*members_to_remove) end end end end puppetlabs-puppet-789f600/lib/puppet/util/windows/com.rb000066400000000000000000000137231470131746300233420ustar00rootroot00000000000000# frozen_string_literal: true require 'ffi' module Puppet::Util::Windows::COM extend FFI::Library ffi_convention :stdcall S_OK = 0 S_FALSE = 1 def SUCCEEDED(hr) hr >= 0 end def FAILED(hr) hr < 0 end module_function :SUCCEEDED, :FAILED def raise_if_hresult_failed(name, *args) failed = FAILED(result = send(name, *args)) and raise _("%{name} failed (hresult %{result}).") % { name: name, result: format('%#08x', result) } result ensure yield failed if block_given? end module_function :raise_if_hresult_failed CLSCTX_INPROC_SERVER = 0x1 CLSCTX_INPROC_HANDLER = 0x2 CLSCTX_LOCAL_SERVER = 0x4 CLSCTX_INPROC_SERVER16 = 0x8 CLSCTX_REMOTE_SERVER = 0x10 CLSCTX_INPROC_HANDLER16 = 0x20 CLSCTX_RESERVED1 = 0x40 CLSCTX_RESERVED2 = 0x80 CLSCTX_RESERVED3 = 0x100 CLSCTX_RESERVED4 = 0x200 CLSCTX_NO_CODE_DOWNLOAD = 0x400 CLSCTX_RESERVED5 = 0x800 CLSCTX_NO_CUSTOM_MARSHAL = 0x1000 CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000 CLSCTX_NO_FAILURE_LOG = 0x4000 CLSCTX_DISABLE_AAA = 0x8000 CLSCTX_ENABLE_AAA = 0x10000 CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000 CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000 CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000 CLSCTX_ENABLE_CLOAKING = 0x100000 CLSCTX_PS_DLL = -0x80000000 CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER # https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615(v=vs.85).aspx # HRESULT CoCreateInstance( # _In_ REFCLSID rclsid, # _In_ LPUNKNOWN pUnkOuter, # _In_ DWORD dwClsContext, # _In_ REFIID riid, # _Out_ LPVOID *ppv # ); ffi_lib :ole32 attach_function_private :CoCreateInstance, [:pointer, :lpunknown, :dword, :pointer, :lpvoid], :hresult # code modified from Unknownr project https://github.com/rpeev/Unknownr # licensed under MIT module Interface def self.[](*args) spec, iid, *ifaces = args.reverse spec.each { |_name, signature| signature[0].unshift(:pointer) } Class.new(FFI::Struct) do const_set(:IID, iid) vtable = Class.new(FFI::Struct) do vtable_hash = (ifaces.map { |iface| iface::VTBL::SPEC.to_a } << spec.to_a).flatten(1).to_h const_set(:SPEC, vtable_hash) layout( *self::SPEC.map { |name, signature| [name, callback(*signature)] }.flatten ) end const_set(:VTBL, vtable) layout \ :lpVtbl, :pointer end end end module Helpers def QueryInstance(klass) instance = nil FFI::MemoryPointer.new(:pointer) do |ppv| QueryInterface(klass::IID, ppv) instance = klass.new(ppv.read_pointer) end begin yield instance return self ensure instance.Release end if block_given? instance end def UseInstance(klass, name, *args) instance = nil FFI::MemoryPointer.new(:pointer) do |ppv| send(name, *args, ppv) yield instance = klass.new(ppv.read_pointer) end self ensure instance.Release if instance && !instance.null? end end module Instance def self.[](iface) Class.new(iface) do send(:include, Helpers) def initialize(pointer) self.pointer = pointer @vtbl = self.class::VTBL.new(self[:lpVtbl]) end attr_reader :vtbl self::VTBL.members.each do |name| define_method(name) do |*args| if Puppet::Util::Windows::COM.FAILED((result = @vtbl[name].call(self, *args))) raise Puppet::Util::Windows::Error.new(_("Failed to call %{klass}::%{name} with HRESULT: %{result}.") % { klass: self, name: name, result: result }, result) end result end end layout \ :lpVtbl, :pointer end end end module Factory def self.[](iface, clsid) Class.new(iface) do send(:include, Helpers) const_set(:CLSID, clsid) def initialize(opts = {}) @opts = opts @opts[:clsctx] ||= CLSCTX_INPROC_SERVER FFI::MemoryPointer.new(:pointer) do |ppv| hr = Puppet::Util::Windows::COM.CoCreateInstance(self.class::CLSID, FFI::Pointer::NULL, @opts[:clsctx], self.class::IID, ppv) if Puppet::Util::Windows::COM.FAILED(hr) raise _("CoCreateInstance failed (%{klass}).") % { klass: self.class } end self.pointer = ppv.read_pointer end @vtbl = self.class::VTBL.new(self[:lpVtbl]) end attr_reader :vtbl self::VTBL.members.each do |name| define_method(name) do |*args| if Puppet::Util::Windows::COM.FAILED((result = @vtbl[name].call(self, *args))) raise Puppet::Util::Windows::Error.new(_("Failed to call %{klass}::%{name} with HRESULT: %{result}.") % { klass: self, name: name, result: result }, result) end result end end layout \ :lpVtbl, :pointer end end end IUnknown = Interface[ FFI::WIN32::GUID['00000000-0000-0000-C000-000000000046'], QueryInterface: [[:pointer, :pointer], :hresult], AddRef: [[], :win32_ulong], Release: [[], :win32_ulong] ] Unknown = Instance[IUnknown] # https://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx # HRESULT CoInitialize( # _In_opt_ LPVOID pvReserved # ); ffi_lib :ole32 attach_function_private :CoInitialize, [:lpvoid], :hresult # https://msdn.microsoft.com/en-us/library/windows/desktop/ms688715(v=vs.85).aspx # void CoUninitialize(void); ffi_lib :ole32 attach_function_private :CoUninitialize, [], :void def InitializeCom raise_if_hresult_failed(:CoInitialize, FFI::Pointer::NULL) at_exit { CoUninitialize() } end module_function :InitializeCom end puppetlabs-puppet-789f600/lib/puppet/util/windows/daemon.rb000066400000000000000000000255251470131746300240320ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/ffi/windows' module Puppet::Util::Windows # The Daemon class, based on the chef/win32-service implementation class Daemon include Puppet::FFI::Windows::Constants extend Puppet::FFI::Windows::Constants include Puppet::FFI::Windows::Structs extend Puppet::FFI::Windows::Structs include Puppet::FFI::Windows::Functions extend Puppet::FFI::Windows::Functions # Service is not running STOPPED = SERVICE_STOPPED # Service has received a start signal but is not yet running START_PENDING = SERVICE_START_PENDING # Service has received a stop signal but is not yet stopped STOP_PENDING = SERVICE_STOP_PENDING # Service is running RUNNING = SERVICE_RUNNING # Service has received a signal to resume but is not yet running CONTINUE_PENDING = SERVICE_CONTINUE_PENDING # Service has received a signal to pause but is not yet paused PAUSE_PENDING = SERVICE_PAUSE_PENDING # Service is paused PAUSED = SERVICE_PAUSED # Service controls # Notifies service that it should stop CONTROL_STOP = SERVICE_CONTROL_STOP # Notifies service that it should pause CONTROL_PAUSE = SERVICE_CONTROL_PAUSE # Notifies service that it should resume CONTROL_CONTINUE = SERVICE_CONTROL_CONTINUE # Notifies service that it should return its current status information CONTROL_INTERROGATE = SERVICE_CONTROL_INTERROGATE # Notifies a service that its parameters have changed CONTROL_PARAMCHANGE = SERVICE_CONTROL_PARAMCHANGE # Notifies a service that there is a new component for binding CONTROL_NETBINDADD = SERVICE_CONTROL_NETBINDADD # Notifies a service that a component for binding has been removed CONTROL_NETBINDREMOVE = SERVICE_CONTROL_NETBINDREMOVE # Notifies a service that a component for binding has been enabled CONTROL_NETBINDENABLE = SERVICE_CONTROL_NETBINDENABLE # Notifies a service that a component for binding has been disabled CONTROL_NETBINDDISABLE = SERVICE_CONTROL_NETBINDDISABLE IDLE = 0 # Misc IDLE_CONTROL_CODE = 0 WAIT_OBJECT_0 = 0 WAIT_TIMEOUT = 0x00000102 WAIT_FAILED = 0xFFFFFFFF NO_ERROR = 0 # Wraps SetServiceStatus. SetTheServiceStatus = proc do |dwCurrentState, dwWin32ExitCode, dwCheckPoint, dwWaitHint| ss = SERVICE_STATUS.new # Current status of the service. # Disable control requests until the service is started. if dwCurrentState == SERVICE_START_PENDING ss[:dwControlsAccepted] = 0 else ss[:dwControlsAccepted] = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_PARAMCHANGE end # Initialize ss structure. ss[:dwServiceType] = SERVICE_WIN32_OWN_PROCESS ss[:dwServiceSpecificExitCode] = 0 ss[:dwCurrentState] = dwCurrentState ss[:dwWin32ExitCode] = dwWin32ExitCode ss[:dwCheckPoint] = dwCheckPoint ss[:dwWaitHint] = dwWaitHint @@dwServiceState = dwCurrentState # Send status of the service to the Service Controller. unless SetServiceStatus(@@ssh, ss) SetEvent(@@hStopEvent) end end ERROR_CALL_NOT_IMPLEMENTED = 0x78 # Handles control signals from the service control manager. Service_Ctrl_ex = proc do |dwCtrlCode, _dwEventType, _lpEventData, _lpContext| @@waiting_control_code = dwCtrlCode; return_value = NO_ERROR begin dwState = SERVICE_RUNNING case dwCtrlCode when SERVICE_CONTROL_STOP dwState = SERVICE_STOP_PENDING when SERVICE_CONTROL_SHUTDOWN dwState = SERVICE_STOP_PENDING when SERVICE_CONTROL_PAUSE dwState = SERVICE_PAUSED when SERVICE_CONTROL_CONTINUE dwState = SERVICE_RUNNING # else # TODO: Handle other control codes? Retain the current state? end # Set the status of the service except on interrogation. unless dwCtrlCode == SERVICE_CONTROL_INTERROGATE SetTheServiceStatus.call(dwState, NO_ERROR, 0, 0) end # Tell service_main thread to stop. if dwCtrlCode == SERVICE_CONTROL_STOP || dwCtrlCode == SERVICE_CONTROL_SHUTDOWN if SetEvent(@@hStopEvent) == 0 SetTheServiceStatus.call(SERVICE_STOPPED, FFI.errno, 0, 0) end end rescue return_value = ERROR_CALL_NOT_IMPLEMENTED end return_value end # Called by the service control manager after the call to StartServiceCtrlDispatcher. Service_Main = FFI::Function.new(:void, [:ulong, :pointer], :blocking => false) do |dwArgc, lpszArgv| # Obtain the name of the service. if lpszArgv.address != 0 argv = lpszArgv.get_array_of_string(0, dwArgc) lpszServiceName = argv[0] else lpszServiceName = '' end # Args passed to Service.start if dwArgc > 1 @@Argv = argv[1..] else @@Argv = nil end # Register the service ctrl handler. @@ssh = RegisterServiceCtrlHandlerExW( lpszServiceName, Service_Ctrl_ex, nil ) # No service to stop, no service handle to notify, nothing to do but exit. break if @@ssh == 0 # The service has started. SetTheServiceStatus.call(SERVICE_RUNNING, NO_ERROR, 0, 0) SetEvent(@@hStartEvent) # Main loop for the service. while WaitForSingleObject(@@hStopEvent, 1000) != WAIT_OBJECT_0 end # Main loop for the service. while WaitForSingleObject(@@hStopCompletedEvent, 1000) != WAIT_OBJECT_0 end ensure # Stop the service. SetTheServiceStatus.call(SERVICE_STOPPED, NO_ERROR, 0, 0) end # This is a shortcut for Daemon.new + Daemon#mainloop. # def self.mainloop new.mainloop end # This is the method that actually puts your code into a loop and allows it # to run as a service. The code that is actually run while in the mainloop # is what you defined in your own Daemon#service_main method. # def mainloop @@waiting_control_code = IDLE_CONTROL_CODE @@dwServiceState = 0 # Redirect STDIN, STDOUT and STDERR to the NUL device if they're still # associated with a tty. This helps newbs avoid Errno::EBADF errors. STDIN.reopen('NUL') if STDIN.isatty STDOUT.reopen('NUL') if STDOUT.isatty STDERR.reopen('NUL') if STDERR.isatty # Calling init here so that init failures never even tries to start the # service. Of course that means that init methods must be very quick # because the SCM will be receiving no START_PENDING messages while # init's running. # # TODO: Fix? service_init() if respond_to?('service_init') # Create the event to signal the service to start. @@hStartEvent = CreateEventW(nil, 1, 0, nil) if @@hStartEvent == 0 raise SystemCallError.new('CreateEvent', FFI.errno) end # Create the event to signal the service to stop. @@hStopEvent = CreateEventW(nil, 1, 0, nil) if @@hStopEvent == 0 raise SystemCallError.new('CreateEvent', FFI.errno) end # Create the event to signal the service that stop has completed @@hStopCompletedEvent = CreateEventW(nil, 1, 0, nil) if @@hStopCompletedEvent == 0 raise SystemCallError.new('CreateEvent', FFI.errno) end hThread = Thread.new do ste = FFI::MemoryPointer.new(SERVICE_TABLE_ENTRYW, 2) s = SERVICE_TABLE_ENTRYW.new(ste[0]) s[:lpServiceName] = FFI::MemoryPointer.from_string("") s[:lpServiceProc] = Service_Main s = SERVICE_TABLE_ENTRYW.new(ste[1]) s[:lpServiceName] = nil s[:lpServiceProc] = nil # No service to step, no service handle, no ruby exceptions, just terminate the thread.. StartServiceCtrlDispatcherW(ste) end while (index = WaitForSingleObject(@@hStartEvent, 1000)) == WAIT_TIMEOUT # The thread exited, so the show is off. raise "Service_Main thread exited abnormally" unless hThread.alive? end if index == WAIT_FAILED raise SystemCallError.new("WaitForSingleObject", FFI.errno) end thr = Thread.new do while WaitForSingleObject(@@hStopEvent, 1000) == WAIT_TIMEOUT # Check to see if anything interesting has been signaled case @@waiting_control_code when SERVICE_CONTROL_PAUSE service_pause() if respond_to?('service_pause') when SERVICE_CONTROL_CONTINUE service_resume() if respond_to?('service_resume') when SERVICE_CONTROL_INTERROGATE service_interrogate() if respond_to?('service_interrogate') when SERVICE_CONTROL_SHUTDOWN service_shutdown() if respond_to?('service_shutdown') when SERVICE_CONTROL_PARAMCHANGE service_paramchange() if respond_to?('service_paramchange') when SERVICE_CONTROL_NETBINDADD service_netbindadd() if respond_to?('service_netbindadd') when SERVICE_CONTROL_NETBINDREMOVE service_netbindremove() if respond_to?('service_netbindremove') when SERVICE_CONTROL_NETBINDENABLE service_netbindenable() if respond_to?('service_netbindenable') when SERVICE_CONTROL_NETBINDDISABLE service_netbinddisable() if respond_to?('service_netbinddisable') end @@waiting_control_code = IDLE_CONTROL_CODE end service_stop() if respond_to?('service_stop') ensure SetEvent(@@hStopCompletedEvent) end if respond_to?('service_main') service_main(*@@Argv) end thr.join end # Returns the state of the service (as an constant integer) which can be any # of the service status constants, e.g. RUNNING, PAUSED, etc. # # This method is typically used within your service_main method to setup the # loop. For example: # # class MyDaemon < Daemon # def service_main # while state == RUNNING || state == PAUSED || state == IDLE # # Your main loop here # end # end # end # # See the Daemon#running? method for an abstraction of the above code. # def state @@dwServiceState end # # Returns whether or not the service is in a running state, i.e. the service # status is either RUNNING, PAUSED or IDLE. # # This is typically used within your service_main method to setup the main # loop. For example: # # class MyDaemon < Daemon # def service_main # while running? # # Your main loop here # end # end # end # def running? [SERVICE_RUNNING, SERVICE_PAUSED, 0].include?(@@dwServiceState) end end end puppetlabs-puppet-789f600/lib/puppet/util/windows/error.rb000066400000000000000000000061651470131746300237170ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/util/windows' require_relative '../../../puppet/error' # represents an error resulting from a Win32 error code class Puppet::Util::Windows::Error < Puppet::Error require 'ffi' extend FFI::Library attr_reader :code # NOTE: FFI.errno only works properly when prior Win32 calls have been made # through FFI bindings. Calls made through Win32API do not have their error # codes captured by FFI.errno def initialize(message, code = FFI.errno, original = nil) super(message + ": #{self.class.format_error_code(code)}", original) @code = code end # Helper method that wraps FormatMessage that returns a human readable string. def self.format_error_code(code) # specifying 0 will look for LANGID in the following order # 1.Language neutral # 2.Thread LANGID, based on the thread's locale value # 3.User default LANGID, based on the user's default locale value # 4.System default LANGID, based on the system default locale value # 5.US English dwLanguageId = 0 flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK error_string = ''.dup # this pointer actually points to a :lpwstr (pointer) since we're letting Windows allocate for us FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr| length = FormatMessageW(flags, FFI::Pointer::NULL, code, dwLanguageId, buffer_ptr, 0, FFI::Pointer::NULL) if length == FFI::WIN32_FALSE # can't raise same error type here or potentially recurse infinitely raise Puppet::Error, _("FormatMessageW could not format code %{code}") % { code: code } end # returns an FFI::Pointer with autorelease set to false, which is what we want buffer_ptr.read_win32_local_pointer do |wide_string_ptr| if wide_string_ptr.null? raise Puppet::Error, _("FormatMessageW failed to allocate buffer for code %{code}") % { code: code } end error_string = wide_string_ptr.read_wide_string(length) end end error_string end ERROR_FILE_NOT_FOUND = 2 ERROR_ACCESS_DENIED = 5 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000 FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000 FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/ms679351(v=vs.85).aspx # DWORD WINAPI FormatMessage( # _In_ DWORD dwFlags, # _In_opt_ LPCVOID lpSource, # _In_ DWORD dwMessageId, # _In_ DWORD dwLanguageId, # _Out_ LPTSTR lpBuffer, # _In_ DWORD nSize, # _In_opt_ va_list *Arguments # ); # NOTE: since we're not preallocating the buffer, use a :pointer for lpBuffer ffi_lib :kernel32 attach_function_private :FormatMessageW, [:dword, :lpcvoid, :dword, :dword, :pointer, :dword, :pointer], :dword end puppetlabs-puppet-789f600/lib/puppet/util/windows/eventlog.rb000066400000000000000000000154461470131746300244130ustar00rootroot00000000000000# frozen_string_literal: true require 'ffi' # Puppet::Util::Windows::EventLog needs to be requirable without having loaded # any other parts of Puppet so it can be leveraged independently by the code # that runs Puppet as a service on Windows. # # For this reason we: # - Define Puppet::Util::Windows # - Replicate logic that exists elsewhere in puppet/util/windows # - Raise generic RuntimeError instead of Puppet::Util::Windows::Error if its not defined module Puppet; module Util; module Windows; end; end; end class Puppet::Util::Windows::EventLog extend FFI::Library # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363679(v=vs.85).aspx EVENTLOG_ERROR_TYPE = 0x0001 EVENTLOG_WARNING_TYPE = 0x0002 EVENTLOG_INFORMATION_TYPE = 0x0004 # These are duplicate definitions from Puppet::Util::Windows::ApiTypes, # established here so this class can be standalone from Puppet, and public so # we can reference them in tests. NULL_HANDLE = 0 WIN32_FALSE = 0 # Register an event log handle for the application # @param source_name [String] the name of the event source to retrieve a handle for # @return [void] # @api public def initialize(source_name = 'Puppet') @eventlog_handle = RegisterEventSourceW(FFI::Pointer::NULL, wide_string(source_name)) if @eventlog_handle == NULL_HANDLE # TRANSLATORS 'Windows' is the operating system and 'RegisterEventSourceW' is a API call and should not be translated raise EventLogError.new(_("RegisterEventSourceW failed to open Windows eventlog"), FFI.errno) end end # Close this instance's event log handle # @return [void] # @api public def close DeregisterEventSource(@eventlog_handle) ensure @eventlog_handle = nil end # Report an event to this instance's event log handle. Accepts a string to # report (:data => ) and event type (:event_type => Integer) and id # (:event_id => Integer) as returned by #to_native. The additional arguments to # ReportEventW seen in this method aren't exposed - though ReportEventW # technically can accept multiple strings as well as raw binary data to log, # we accept a single string from Puppet::Util::Log # # @param args [Hash{Symbol=>Object}] options to the associated log event # @return [void] # @api public def report_event(args = {}) unless args[:data].is_a?(String) raise ArgumentError, _("data must be a string, not %{class_name}") % { class_name: args[:data].class } end from_string_to_wide_string(args[:data]) do |message_ptr| FFI::MemoryPointer.new(:pointer) do |message_array_ptr| message_array_ptr.write_pointer(message_ptr) user_sid = FFI::Pointer::NULL raw_data = FFI::Pointer::NULL raw_data_size = 0 num_strings = 1 eventlog_category = 0 report_result = ReportEventW(@eventlog_handle, args[:event_type], eventlog_category, args[:event_id], user_sid, num_strings, raw_data_size, message_array_ptr, raw_data) if report_result == WIN32_FALSE # TRANSLATORS 'Windows' is the operating system and 'ReportEventW' is a API call and should not be translated raise EventLogError.new(_("ReportEventW failed to report event to Windows eventlog"), FFI.errno) end end end end class << self # Feels more natural to do Puppet::Util::Window::EventLog.open("MyApplication") alias :open :new # Query event identifier info for a given log level # @param level [Symbol] an event log level # @return [Array] Win API Event ID, Puppet Event ID # @api public def to_native(level) case level when :debug, :info, :notice [EVENTLOG_INFORMATION_TYPE, 0x01] when :warning [EVENTLOG_WARNING_TYPE, 0x02] when :err, :alert, :emerg, :crit [EVENTLOG_ERROR_TYPE, 0x03] else raise ArgumentError, _("Invalid log level %{level}") % { level: level } end end end private # For the purposes of allowing this class to be standalone, the following are # duplicate definitions from elsewhere in Puppet: # If we're loaded via Puppet we should keep the previous behavior of raising # Puppet::Util::Windows::Error on errors. If we aren't, at least concatenate # the error code to the exception message to pass this information on to the # user if defined?(Puppet::Util::Windows::Error) EventLogError = Puppet::Util::Windows::Error else class EventLogError < RuntimeError def initialize(msg, code) # TRANSLATORS 'Win32' is the Windows API and should not be translated super(msg + ' ' + _("(Win32 error: %{detail})") % { detail: code }) end end end # Private duplicate of Puppet::Util::Windows::String::wide_string # Not for use outside of EventLog! - use Puppet::Util::Windows instead # @api private def wide_string(str) # if given a nil string, assume caller wants to pass a nil pointer to win32 return nil if str.nil? str.encode('UTF-16LE') end # Private duplicate of Puppet::Util::Windows::ApiTypes::from_string_to_wide_string # Not for use outside of EventLog! - Use Puppet::Util::Windows instead # @api private def from_string_to_wide_string(str, &block) str = wide_string(str) FFI::MemoryPointer.from_wide_string(str) { |ptr| yield ptr } # ptr has already had free called, so nothing to return nil end ffi_convention :stdcall # The following are typedefs in Puppet::Util::Winodws::ApiTypes, but here we # use their original FFI counterparts: # :uintptr_t for :handle # :int32 for :win32_bool # :uint16 for :word # :uint32 for :dword # :pointer for :lpvoid # :uchar for :byte # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363678(v=vs.85).aspx # HANDLE RegisterEventSource( # _In_ LPCTSTR lpUNCServerName, # _In_ LPCTSTR lpSourceName # ); ffi_lib :advapi32 attach_function :RegisterEventSourceW, [:buffer_in, :buffer_in], :uintptr_t private :RegisterEventSourceW # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363642(v=vs.85).aspx # BOOL DeregisterEventSource( # _Inout_ HANDLE hEventLog # ); ffi_lib :advapi32 attach_function :DeregisterEventSource, [:uintptr_t], :int32 private :DeregisterEventSource # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363679(v=vs.85).aspx # BOOL ReportEvent( # _In_ HANDLE hEventLog, # _In_ WORD wType, # _In_ WORD wCategory, # _In_ DWORD dwEventID, # _In_ PSID lpUserSid, # _In_ WORD wNumStrings, # _In_ DWORD dwDataSize, # _In_ LPCTSTR *lpStrings, # _In_ LPVOID lpRawData # ); ffi_lib :advapi32 attach_function :ReportEventW, [:uintptr_t, :uint16, :uint16, :uint32, :pointer, :uint16, :uint32, :pointer, :pointer], :int32 private :ReportEventW end puppetlabs-puppet-789f600/lib/puppet/util/windows/file.rb000066400000000000000000000262341470131746300235040ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/ffi/windows' module Puppet::Util::Windows::File extend Puppet::Util::Windows::String include Puppet::FFI::Windows::Constants extend Puppet::FFI::Windows::Structs include Puppet::FFI::Windows::Structs include Puppet::FFI::Windows::Functions extend Puppet::FFI::Windows::Functions def replace_file(target, source) target_encoded = wide_string(target.to_s) source_encoded = wide_string(source.to_s) flags = REPLACEFILE_IGNORE_MERGE_ERRORS backup_file = nil result = ReplaceFileW( target_encoded, source_encoded, backup_file, flags, FFI::Pointer::NULL, FFI::Pointer::NULL ) return true if result != FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "ReplaceFile(#{target}, #{source})" end module_function :replace_file def move_file_ex(source, target, flags = 0) result = MoveFileExW(wide_string(source.to_s), wide_string(target.to_s), flags) return true if result != FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})" end module_function :move_file_ex def symlink(target, symlink) flags = File.directory?(target) ? 0x1 : 0x0 result = CreateSymbolicLinkW(wide_string(symlink.to_s), wide_string(target.to_s), flags) return true if result != FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "CreateSymbolicLink(#{symlink}, #{target}, #{flags.to_s(8)})" end module_function :symlink def exist?(path) path = path.to_str if path.respond_to?(:to_str) # support WatchedFile path = path.to_s # support String and Pathname seen_paths = [] # follow up to 64 symlinks before giving up 0.upto(64) do |_depth| # return false if this path has been seen before. This is protection against circular symlinks return false if seen_paths.include?(path.downcase) result = get_attributes(path, false) # return false for path not found return false if result == INVALID_FILE_ATTRIBUTES # return true if path exists and it's not a symlink # Other file attributes are ignored. https://msdn.microsoft.com/en-us/library/windows/desktop/gg258117(v=vs.85).aspx reparse_point = (result & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT if reparse_point && symlink_reparse_point?(path) # walk the symlink and try again... seen_paths << path.downcase path = readlink(path) else # file was found and its not a symlink return true end end false end module_function :exist? def get_attributes(file_name, raise_on_invalid = true) result = GetFileAttributesW(wide_string(file_name.to_s)) if raise_on_invalid && result == INVALID_FILE_ATTRIBUTES raise Puppet::Util::Windows::Error, "GetFileAttributes(#{file_name})" end result end module_function :get_attributes def add_attributes(path, flags) oldattrs = get_attributes(path) if (oldattrs | flags) != oldattrs set_attributes(path, oldattrs | flags) end end module_function :add_attributes def remove_attributes(path, flags) oldattrs = get_attributes(path) if (oldattrs & ~flags) != oldattrs set_attributes(path, oldattrs & ~flags) end end module_function :remove_attributes def set_attributes(path, flags) success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to set file attributes") unless success success end module_function :set_attributes # define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address def self.create_file(file_name, desired_access, share_mode, security_attributes, creation_disposition, flags_and_attributes, template_file_handle) result = CreateFileW(wide_string(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, "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.get_reparse_point_data(handle, &block) # must be multiple of 1024, min 10240 FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) do |reparse_data_buffer_ptr| device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr) reparse_tag = reparse_data_buffer_ptr.read_win32_ulong buffer_type = case reparse_tag when IO_REPARSE_TAG_SYMLINK SYMLINK_REPARSE_DATA_BUFFER when IO_REPARSE_TAG_MOUNT_POINT MOUNT_POINT_REPARSE_DATA_BUFFER when IO_REPARSE_TAG_NFS raise Puppet::Util::Windows::Error, "Retrieving NFS reparse point data is unsupported" else raise Puppet::Util::Windows::Error, "DeviceIoControl(#{handle}, " \ "FSCTL_GET_REPARSE_POINT) returned unknown tag 0x#{reparse_tag.to_s(16).upcase}" end yield buffer_type.new(reparse_data_buffer_ptr) end # underlying struct MemoryPointer has been cleaned up by this point, nothing to return nil end def self.get_reparse_point_tag(handle) reparse_tag = nil # must be multiple of 1024, min 10240 FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) do |reparse_data_buffer_ptr| device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr) # DWORD ReparseTag is the first member of the struct reparse_tag = reparse_data_buffer_ptr.read_win32_ulong end reparse_tag 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, _("out_buffer is required") end FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr| result = DeviceIoControl( handle, io_control_code, in_buffer, in_buffer.nil? ? 0 : in_buffer.size, out_buffer, out_buffer.size, bytes_returned_ptr, nil ) if result == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "DeviceIoControl(#{handle}, #{io_control_code}, " \ "#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, " \ "#{out_buffer}, #{out_buffer ? out_buffer.size : ''}" end end out_buffer end def reparse_point?(file_name) attributes = get_attributes(file_name, false) return false if attributes == INVALID_FILE_ATTRIBUTES (attributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT end module_function :reparse_point? def symlink?(file_name) # Puppet currently only handles mount point and symlink reparse points, ignores others reparse_point?(file_name) && symlink_reparse_point?(file_name) end module_function :symlink? def self.open_symlink(link_name) begin yield handle = create_file( link_name, GENERIC_READ, FILE_SHARE_READ, nil, # security_attributes OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0 ) # template_file ensure FFI::WIN32.CloseHandle(handle) if handle end # handle has had CloseHandle called against it, so nothing to return nil end def readlink(link_name) link = nil open_symlink(link_name) do |handle| link = resolve_symlink(handle) end link end module_function :readlink def get_long_pathname(path) converted = ''.dup FFI::Pointer.from_string_to_wide_string(path) do |path_ptr| # includes terminating NULL buffer_size = GetLongPathNameW(path_ptr, FFI::Pointer::NULL, 0) FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr| if GetLongPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to call GetLongPathName") end converted = converted_ptr.read_wide_string(buffer_size - 1) end end converted end module_function :get_long_pathname def get_short_pathname(path) converted = ''.dup FFI::Pointer.from_string_to_wide_string(path) do |path_ptr| # includes terminating NULL buffer_size = GetShortPathNameW(path_ptr, FFI::Pointer::NULL, 0) FFI::MemoryPointer.new(:wchar, buffer_size) do |converted_ptr| if GetShortPathNameW(path_ptr, converted_ptr, buffer_size) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "Failed to call GetShortPathName" end converted = converted_ptr.read_wide_string(buffer_size - 1) end end converted end module_function :get_short_pathname def stat(file_name) file_name = file_name.to_s # accommodate PathName or String stat = File.stat(file_name) singleton_class = class << stat; self; end target_path = file_name if symlink?(file_name) target_path = readlink(file_name) link_ftype = File.stat(target_path).ftype # sigh, monkey patch instance method for instance, and close over link_ftype singleton_class.send(:define_method, :ftype) do link_ftype end end singleton_class.send(:define_method, :mode) do Puppet::Util::Windows::Security.get_mode(target_path) end stat end module_function :stat def lstat(file_name) file_name = file_name.to_s # accommodate PathName or String # monkey'ing around! stat = File.lstat(file_name) singleton_class = class << stat; self; end singleton_class.send(:define_method, :mode) do Puppet::Util::Windows::Security.get_mode(file_name) end if symlink?(file_name) def stat.ftype "link" end end stat end module_function :lstat def self.resolve_symlink(handle) path = nil get_reparse_point_data(handle) do |reparse_data| offset = reparse_data[:PrintNameOffset] length = reparse_data[:PrintNameLength] ptr = reparse_data.pointer + reparse_data.offset_of(:PathBuffer) + offset path = ptr.read_wide_string(length / 2) # length is bytes, need UTF-16 wchars end path end private_class_method :resolve_symlink # these reparse point types are the only ones Puppet currently understands # so rather than raising an exception in readlink, prefer to not consider # the path a symlink when stat'ing later def self.symlink_reparse_point?(path) symlink = false open_symlink(path) do |handle| symlink = [ IO_REPARSE_TAG_SYMLINK, IO_REPARSE_TAG_MOUNT_POINT ].include?(get_reparse_point_tag(handle)) end symlink end private_class_method :symlink_reparse_point? end puppetlabs-puppet-789f600/lib/puppet/util/windows/monkey_patches/000077500000000000000000000000001470131746300252425ustar00rootroot00000000000000puppetlabs-puppet-789f600/lib/puppet/util/windows/monkey_patches/process.rb000066400000000000000000000313341470131746300272510ustar00rootroot00000000000000# frozen_string_literal: true require 'ffi' require_relative '../../../../puppet/ffi/windows' require_relative '../../../../puppet/util/windows/string' module Process extend FFI::Library extend Puppet::Util::Windows::String extend Puppet::FFI::Windows::APITypes extend Puppet::FFI::Windows::Functions extend Puppet::FFI::Windows::Structs include Puppet::FFI::Windows::Constants include Puppet::FFI::Windows::Structs ProcessInfo = Struct.new( 'ProcessInfo', :process_handle, :thread_handle, :process_id, :thread_id ) private_constant :ProcessInfo # Disable popups. This mostly affects the Process.kill method. SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX) class << self private :SetHandleInformation, :SetErrorMode, :CreateProcessW, :OpenProcess, :SetPriorityClass, :CreateProcessWithLogonW, :get_osfhandle, :get_errno # Process.create(key => value, ...) => ProcessInfo # # This is a wrapper for the CreateProcess() function. It executes a process, # returning a ProcessInfo struct. It accepts a hash as an argument. # There are several primary keys: # # * command_line (this or app_name must be present) # * app_name (default: nil) # * inherit (default: false) # * process_inherit (default: false) # * thread_inherit (default: false) # * creation_flags (default: 0) # * cwd (default: Dir.pwd) # * startup_info (default: nil) # * environment (default: nil) # * close_handles (default: true) # * with_logon (default: nil) # * domain (default: nil) # * password (default: nil, mandatory if with_logon) # # Of these, the 'command_line' or 'app_name' must be specified or an # error is raised. Both may be set individually, but 'command_line' should # be preferred if only one of them is set because it does not (necessarily) # require an explicit path or extension to work. # # The 'domain' and 'password' options are only relevent in the context # of 'with_logon'. If 'with_logon' is set, then the 'password' option is # mandatory. # # The startup_info key takes a hash. Its keys are attributes that are # part of the StartupInfo struct, and are generally only meaningful for # GUI or console processes. See the documentation on CreateProcess() # and the StartupInfo struct on MSDN for more information. # # * desktop # * title # * x # * y # * x_size # * y_size # * x_count_chars # * y_count_chars # * fill_attribute # * sw_flags # * startf_flags # * stdin # * stdout # * stderr # # Note that the 'stdin', 'stdout' and 'stderr' options can be either Ruby # IO objects or file descriptors (i.e. a fileno). However, StringIO objects # are not currently supported. Unfortunately, setting these is not currently # an option for JRuby. # # If 'stdin', 'stdout' or 'stderr' are specified, then the +inherit+ value # is automatically set to true and the Process::STARTF_USESTDHANDLES flag is # automatically OR'd to the +startf_flags+ value. # # The ProcessInfo struct contains the following members: # # * process_handle - The handle to the newly created process. # * thread_handle - The handle to the primary thread of the process. # * process_id - Process ID. # * thread_id - Thread ID. # # If the 'close_handles' option is set to true (the default) then the # process_handle and the thread_handle are automatically closed for you # before the ProcessInfo struct is returned. # # If the 'with_logon' option is set, then the process runs the specified # executable file in the security context of the specified credentials. VALID_KEYS = %i[ app_name command_line inherit creation_flags cwd environment startup_info thread_inherit process_inherit close_handles with_logon domain password ].freeze VALID_SI_KEYS = %i[ startf_flags desktop title x y x_size y_size x_count_chars y_count_chars fill_attribute sw_flags stdin stdout stderr ].freeze private_constant :VALID_KEYS, :VALID_SI_KEYS def create(args) # Validate that args is a Hash validate_args(args) initialize_defaults # Validate the keys, and convert symbols and case to lowercase strings. validate_keys(args) # If the startup_info key is present, validate its subkeys validate_startup_info if hash[:startup_info] # validates that 'app_name' or 'command_line' is set validate_command_line if hash[:app_name] && !hash[:command_line] hash[:command_line] = hash[:app_name] hash[:app_name] = nil end # Setup stdin, stdout and stderr handlers setup_std_handlers if logon create_process_with_logon else create_process end # Automatically close the process and thread handles in the # PROCESS_INFORMATION struct unless explicitly told not to. if hash[:close_handles] FFI::WIN32.CloseHandle(procinfo[:hProcess]) FFI::WIN32.CloseHandle(procinfo[:hThread]) end ProcessInfo.new( procinfo[:hProcess], procinfo[:hThread], procinfo[:dwProcessId], procinfo[:dwThreadId] ) end remove_method :setpriority # Sets the priority class for the specified process id +int+. # # The +kind+ parameter is ignored but present for API compatibility. # You can only retrieve process information, not process group or user # information, so it is effectively always Process::PRIO_PROCESS. # # Possible +int_priority+ values are: # # * Process::NORMAL_PRIORITY_CLASS # * Process::IDLE_PRIORITY_CLASS # * Process::HIGH_PRIORITY_CLASS # * Process::REALTIME_PRIORITY_CLASS # * Process::BELOW_NORMAL_PRIORITY_CLASS # * Process::ABOVE_NORMAL_PRIORITY_CLASS def setpriority(kind, int, int_priority) raise TypeError unless kind.is_a?(Integer) raise TypeError unless int.is_a?(Integer) raise TypeError unless int_priority.is_a?(Integer) int = Process.pid if int == 0 handle = OpenProcess(PROCESS_SET_INFORMATION, 0, int) if handle == 0 raise SystemCallError, FFI.errno, "OpenProcess" end begin result = SetPriorityClass(handle, int_priority) raise SystemCallError, FFI.errno, "SetPriorityClass" unless result ensure FFI::WIN32.CloseHandle(handle) end 0 end private def initialize_defaults @hash = { app_name: nil, creation_flags: 0, close_handles: true } @si_hash = nil @procinfo = nil end def validate_args(args) raise TypeError, 'hash keyword arguments expected' unless args.is_a?(Hash) end def validate_keys(args) args.each do |key, val| key = key.to_s.to_sym raise ArgumentError, "invalid key '#{key}'" unless VALID_KEYS.include?(key) hash[key] = val end end def validate_startup_info hash[:startup_info].each do |key, val| key = key.to_s.to_sym raise ArgumentError, "invalid startup_info key '#{key}'" unless VALID_SI_KEYS.include?(key) si_hash[key] = val end end def validate_command_line raise ArgumentError, 'command_line or app_name must be specified' unless hash[:app_name] || hash[:command_line] end def procinfo @procinfo ||= PROCESS_INFORMATION.new end def hash @hash ||= {} end def si_hash @si_hash ||= {} end def app wide_string(hash[:app_name]) end def cmd wide_string(hash[:command_line]) end def cwd wide_string(hash[:cwd]) end def password wide_string(hash[:password]) end def logon wide_string(hash[:with_logon]) end def domain wide_string(hash[:domain]) end def env env = hash[:environment] return unless env env = env.split(File::PATH_SEPARATOR) unless env.respond_to?(:join) env = env.map { |e| e + 0.chr }.join('') + 0.chr env = wide_string(env) if hash[:with_logon] env end def process_security return unless hash[:process_inherit] process_security = SECURITY_ATTRIBUTES.new process_security[:nLength] = SECURITY_ATTRIBUTES.size process_security[:bInheritHandle] = 1 process_security end def thread_security return unless hash[:thread_inherit] thread_security = SECURITY_ATTRIBUTES.new thread_security[:nLength] = SECURITY_ATTRIBUTES.size thread_security[:bInheritHandle] = 1 thread_security end # Automatically handle stdin, stdout and stderr as either IO objects # or file descriptors. This won't work for StringIO, however. It also # will not work on JRuby because of the way it handles internal file # descriptors. def setup_std_handlers %i[stdin stdout stderr].each do |io| next unless si_hash[io] handle = if si_hash[io].respond_to?(:fileno) get_osfhandle(si_hash[io].fileno) else get_osfhandle(si_hash[io]) end if handle == INVALID_HANDLE_VALUE ptr = FFI::MemoryPointer.new(:int) errno = if get_errno(ptr).zero? ptr.read_int else FFI.errno end raise SystemCallError.new('get_osfhandle', errno) end # Most implementations of Ruby on Windows create inheritable # handles by default, but some do not. RF bug #26988. bool = SetHandleInformation( handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT ) raise SystemCallError.new('SetHandleInformation', FFI.errno) unless bool si_hash[io] = handle si_hash[:startf_flags] ||= 0 si_hash[:startf_flags] |= STARTF_USESTDHANDLES hash[:inherit] = true end end def startinfo startinfo = STARTUPINFO.new return startinfo if si_hash.empty? startinfo[:cb] = startinfo.size startinfo[:lpDesktop] = si_hash[:desktop] if si_hash[:desktop] startinfo[:lpTitle] = si_hash[:title] if si_hash[:title] startinfo[:dwX] = si_hash[:x] if si_hash[:x] startinfo[:dwY] = si_hash[:y] if si_hash[:y] startinfo[:dwXSize] = si_hash[:x_size] if si_hash[:x_size] startinfo[:dwYSize] = si_hash[:y_size] if si_hash[:y_size] startinfo[:dwXCountChars] = si_hash[:x_count_chars] if si_hash[:x_count_chars] startinfo[:dwYCountChars] = si_hash[:y_count_chars] if si_hash[:y_count_chars] startinfo[:dwFillAttribute] = si_hash[:fill_attribute] if si_hash[:fill_attribute] startinfo[:dwFlags] = si_hash[:startf_flags] if si_hash[:startf_flags] startinfo[:wShowWindow] = si_hash[:sw_flags] if si_hash[:sw_flags] startinfo[:cbReserved2] = 0 startinfo[:hStdInput] = si_hash[:stdin] if si_hash[:stdin] startinfo[:hStdOutput] = si_hash[:stdout] if si_hash[:stdout] startinfo[:hStdError] = si_hash[:stderr] if si_hash[:stderr] startinfo end def create_process_with_logon raise ArgumentError, 'password must be specified if with_logon is used' unless password hash[:creation_flags] |= CREATE_UNICODE_ENVIRONMENT bool = CreateProcessWithLogonW( logon, # User domain, # Domain password, # Password LOGON_WITH_PROFILE, # Logon flags app, # App name cmd, # Command line hash[:creation_flags], # Creation flags env, # Environment cwd, # Working directory startinfo, # Startup Info procinfo # Process Info ) raise SystemCallError.new('CreateProcessWithLogonW', FFI.errno) unless bool end def create_process inherit = hash[:inherit] ? 1 : 0 bool = CreateProcessW( app, # App name cmd, # Command line process_security, # Process attributes thread_security, # Thread attributes inherit, # Inherit handles? hash[:creation_flags], # Creation flags env, # Environment cwd, # Working directory startinfo, # Startup Info procinfo # Process Info ) raise SystemCallError.new('CreateProcess', FFI.errno) unless bool end end end puppetlabs-puppet-789f600/lib/puppet/util/windows/principal.rb000066400000000000000000000215771470131746300245530ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/util/windows' module Puppet::Util::Windows::SID class Principal extend FFI::Library attr_reader :account, :sid_bytes, :sid, :domain, :domain_account, :account_type def initialize(account, sid_bytes, sid, domain, account_type) # This is only ever called from lookup_account_sid which has already # removed the potential for passing in an account like host\user @account = account @sid_bytes = sid_bytes @sid = sid @domain = domain @account_type = account_type # When domain is available and it is a Domain principal, use domain only # otherwise if domain is available then combine it with parsed account # otherwise when the domain is not available, use the account value directly # WinNT naming standard https://msdn.microsoft.com/en-us/library/windows/desktop/aa746534(v=vs.85).aspx if domain && !domain.empty? && @account_type == :SidTypeDomain @domain_account = @domain elsif domain && !domain.empty? @domain_account = "#{domain}\\#{@account}" else @domain_account = account end end # added for backward compatibility def ==(compare) compare.is_a?(Puppet::Util::Windows::SID::Principal) && @sid_bytes == compare.sid_bytes end # returns authority qualified account name # prefer to compare Principal instances with == operator or by #sid def to_s @domain_account end # = 8 + max sub identifiers (15) * 4 MAXIMUM_SID_BYTE_LENGTH = 68 ERROR_INVALID_PARAMETER = 87 ERROR_INSUFFICIENT_BUFFER = 122 def self.lookup_account_name(system_name = nil, sanitize = true, account_name) account_name = sanitize_account_name(account_name) if sanitize system_name_ptr = FFI::Pointer::NULL begin if system_name system_name_wide = Puppet::Util::Windows::String.wide_string(system_name) system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide) end FFI::MemoryPointer.from_string_to_wide_string(account_name) do |account_name_ptr| FFI::MemoryPointer.new(:byte, MAXIMUM_SID_BYTE_LENGTH) do |sid_ptr| FFI::MemoryPointer.new(:dword, 1) do |sid_length_ptr| FFI::MemoryPointer.new(:dword, 1) do |domain_length_ptr| FFI::MemoryPointer.new(:uint32, 1) do |name_use_enum_ptr| sid_length_ptr.write_dword(MAXIMUM_SID_BYTE_LENGTH) success = LookupAccountNameW(system_name_ptr, account_name_ptr, sid_ptr, sid_length_ptr, FFI::Pointer::NULL, domain_length_ptr, name_use_enum_ptr) last_error = FFI.errno if success == FFI::WIN32_FALSE && last_error != ERROR_INSUFFICIENT_BUFFER raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountNameW with account: %{account_name}') % { account_name: account_name }, last_error) end FFI::MemoryPointer.new(:lpwstr, domain_length_ptr.read_dword) do |domain_ptr| if LookupAccountNameW(system_name_ptr, account_name_ptr, sid_ptr, sid_length_ptr, domain_ptr, domain_length_ptr, name_use_enum_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _('Failed to call LookupAccountNameW with account: %{account_name}') % { account_name: account_name } end # with a SID returned, loop back through lookup_account_sid to retrieve official name # necessary when accounts like . or '' are passed in return lookup_account_sid( system_name, sid_ptr.read_bytes(sid_length_ptr.read_dword).unpack('C*') ) end end end end end end ensure system_name_ptr.free if system_name_ptr != FFI::Pointer::NULL end end def self.lookup_account_sid(system_name = nil, sid_bytes) system_name_ptr = FFI::Pointer::NULL if sid_bytes.nil? || (!sid_bytes.is_a? Array) || (sid_bytes.length == 0) # TRANSLATORS `lookup_account_sid` is a variable name and should not be translated raise Puppet::Util::Windows::Error, _('Byte array for lookup_account_sid must not be nil and must be at least 1 byte long') end begin if system_name system_name_wide = Puppet::Util::Windows::String.wide_string(system_name) system_name_ptr = FFI::MemoryPointer.from_wide_string(system_name_wide) end FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr| FFI::MemoryPointer.new(:dword, 1) do |name_length_ptr| FFI::MemoryPointer.new(:dword, 1) do |domain_length_ptr| FFI::MemoryPointer.new(:uint32, 1) do |name_use_enum_ptr| sid_ptr.write_array_of_uchar(sid_bytes) if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error.new(_('Byte array for lookup_account_sid is invalid: %{sid_bytes}') % { sid_bytes: sid_bytes }, ERROR_INVALID_PARAMETER) end success = LookupAccountSidW(system_name_ptr, sid_ptr, FFI::Pointer::NULL, name_length_ptr, FFI::Pointer::NULL, domain_length_ptr, name_use_enum_ptr) last_error = FFI.errno if success == FFI::WIN32_FALSE && last_error != ERROR_INSUFFICIENT_BUFFER raise Puppet::Util::Windows::Error.new(_('Failed to call LookupAccountSidW with bytes: %{sid_bytes}') % { sid_bytes: sid_bytes }, last_error) end FFI::MemoryPointer.new(:lpwstr, name_length_ptr.read_dword) do |name_ptr| FFI::MemoryPointer.new(:lpwstr, domain_length_ptr.read_dword) do |domain_ptr| if LookupAccountSidW(system_name_ptr, sid_ptr, name_ptr, name_length_ptr, domain_ptr, domain_length_ptr, name_use_enum_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _('Failed to call LookupAccountSidW with bytes: %{sid_bytes}') % { sid_bytes: sid_bytes } end return new( name_ptr.read_wide_string(name_length_ptr.read_dword), sid_bytes, Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr), domain_ptr.read_wide_string(domain_length_ptr.read_dword), SID_NAME_USE[name_use_enum_ptr.read_uint32] ) end end end end end end ensure system_name_ptr.free if system_name_ptr != FFI::Pointer::NULL end end # Sanitize the given account name for lookup to avoid known issues def self.sanitize_account_name(account_name) return account_name unless account_name.start_with?('APPLICATION PACKAGE AUTHORITY\\') account_name.split('\\').last end private_class_method :sanitize_account_name ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379601(v=vs.85).aspx SID_NAME_USE = enum( :SidTypeUser, 1, :SidTypeGroup, 2, :SidTypeDomain, 3, :SidTypeAlias, 4, :SidTypeWellKnownGroup, 5, :SidTypeDeletedAccount, 6, :SidTypeInvalid, 7, :SidTypeUnknown, 8, :SidTypeComputer, 9, :SidTypeLabel, 10 ) # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379159(v=vs.85).aspx # BOOL WINAPI LookupAccountName( # _In_opt_ LPCTSTR lpSystemName, # _In_ LPCTSTR lpAccountName, # _Out_opt_ PSID Sid, # _Inout_ LPDWORD cbSid, # _Out_opt_ LPTSTR ReferencedDomainName, # _Inout_ LPDWORD cchReferencedDomainName, # _Out_ PSID_NAME_USE peUse # ); ffi_lib :advapi32 attach_function_private :LookupAccountNameW, [:lpcwstr, :lpcwstr, :pointer, :lpdword, :lpwstr, :lpdword, :pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379166(v=vs.85).aspx # BOOL WINAPI LookupAccountSid( # _In_opt_ LPCTSTR lpSystemName, # _In_ PSID lpSid, # _Out_opt_ LPTSTR lpName, # _Inout_ LPDWORD cchName, # _Out_opt_ LPTSTR lpReferencedDomainName, # _Inout_ LPDWORD cchReferencedDomainName, # _Out_ PSID_NAME_USE peUse # ); ffi_lib :advapi32 attach_function_private :LookupAccountSidW, [:lpcwstr, :pointer, :lpwstr, :lpdword, :lpwstr, :lpdword, :pointer], :win32_bool end end puppetlabs-puppet-789f600/lib/puppet/util/windows/process.rb000066400000000000000000000306621470131746300242430ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/util/windows/monkey_patches/process' require_relative '../../../puppet/ffi/windows' module Puppet::Util::Windows::Process extend Puppet::FFI::Windows::Functions include Puppet::FFI::Windows::Structs extend Puppet::Util::Windows::String WAIT_TIMEOUT = 0x102 WAIT_INTERVAL = 200 # https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-creation-flags CREATE_NO_WINDOW = 0x08000000 # https://docs.microsoft.com/en-us/windows/desktop/ProcThread/process-security-and-access-rights PROCESS_QUERY_INFORMATION = 0x0400 # https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation MAX_PATH_LENGTH = 32_767 def execute(command, arguments, stdin, stdout, stderr) create_args = { :command_line => command, :startup_info => { :stdin => stdin, :stdout => stdout, :stderr => stderr }, :close_handles => false, } if arguments[:suppress_window] create_args[:creation_flags] = CREATE_NO_WINDOW end if arguments[:cwd] create_args[:cwd] = arguments[:cwd] end Process.create(create_args) end module_function :execute def wait_process(handle) while WaitForSingleObject(handle, WAIT_INTERVAL) == WAIT_TIMEOUT sleep(0) end exit_status = -1 FFI::MemoryPointer.new(:dword, 1) do |exit_status_ptr| if GetExitCodeProcess(handle, exit_status_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to get child process exit code") end exit_status = exit_status_ptr.read_dword # $CHILD_STATUS is not set when calling win32/process Process.create # and since it's read-only, we can't set it. But we can execute a # a shell that simply returns the desired exit status, which has the # desired effect. %x(#{ENV.fetch('COMSPEC', nil)} /c exit #{exit_status}) end exit_status end module_function :wait_process def get_current_process # this pseudo-handle does not require closing per MSDN docs GetCurrentProcess() end module_function :get_current_process def open_process(desired_access, inherit_handle, process_id, &block) phandle = nil inherit = inherit_handle ? FFI::WIN32_TRUE : FFI::WIN32_FALSE begin phandle = OpenProcess(desired_access, inherit, process_id) if phandle == FFI::Pointer::NULL_HANDLE raise Puppet::Util::Windows::Error, "OpenProcess(#{desired_access.to_s(8)}, #{inherit}, #{process_id})" end yield phandle ensure FFI::WIN32.CloseHandle(phandle) if phandle end # phandle has had CloseHandle called against it, so nothing to return nil end module_function :open_process def open_process_token(handle, desired_access, &block) token_handle = nil begin FFI::MemoryPointer.new(:handle, 1) do |token_handle_ptr| result = OpenProcessToken(handle, desired_access, token_handle_ptr) if result == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "OpenProcessToken(#{handle}, #{desired_access.to_s(8)}, #{token_handle_ptr})" end yield token_handle = token_handle_ptr.read_handle end token_handle ensure FFI::WIN32.CloseHandle(token_handle) if token_handle end # token_handle has had CloseHandle called against it, so nothing to return nil end module_function :open_process_token # Execute a block with the current process token def with_process_token(access, &block) handle = get_current_process open_process_token(handle, access) do |token_handle| yield token_handle end # all handles have been closed, so nothing to safely return nil end module_function :with_process_token def get_process_image_name_by_pid(pid) image_name = ''.dup Puppet::Util::Windows::Security.with_privilege(Puppet::Util::Windows::Security::SE_DEBUG_NAME) do open_process(PROCESS_QUERY_INFORMATION, false, pid) do |phandle| FFI::MemoryPointer.new(:dword, 1) do |exe_name_length_ptr| # UTF is 2 bytes/char: max_chars = MAX_PATH_LENGTH + 1 exe_name_length_ptr.write_dword(max_chars) FFI::MemoryPointer.new(:wchar, max_chars) do |exe_name_ptr| use_win32_path_format = 0 result = QueryFullProcessImageNameW(phandle, use_win32_path_format, exe_name_ptr, exe_name_length_ptr) if result == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "QueryFullProcessImageNameW(phandle, #{use_win32_path_format}, " \ "exe_name_ptr, #{max_chars}" end image_name = exe_name_ptr.read_wide_string(exe_name_length_ptr.read_dword) end end end end image_name end module_function :get_process_image_name_by_pid def lookup_privilege_value(name, system_name = '', &block) FFI::MemoryPointer.new(LUID.size) do |luid_ptr| result = LookupPrivilegeValueW( wide_string(system_name), wide_string(name.to_s), luid_ptr ) if result == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "LookupPrivilegeValue(#{system_name}, #{name}, #{luid_ptr})" end yield LUID.new(luid_ptr) end # the underlying MemoryPointer for LUID is cleaned up by this point nil end module_function :lookup_privilege_value def get_token_information(token_handle, token_information, &block) # to determine buffer size FFI::MemoryPointer.new(:dword, 1) do |return_length_ptr| result = GetTokenInformation(token_handle, token_information, nil, 0, return_length_ptr) return_length = return_length_ptr.read_dword if return_length <= 0 raise Puppet::Util::Windows::Error, "GetTokenInformation(#{token_handle}, #{token_information}, nil, 0, #{return_length_ptr})" end # re-call API with properly sized buffer for all results FFI::MemoryPointer.new(return_length) do |token_information_buf| result = GetTokenInformation(token_handle, token_information, token_information_buf, return_length, return_length_ptr) if result == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, "GetTokenInformation(#{token_handle}, #{token_information}, #{token_information_buf}, " \ "#{return_length}, #{return_length_ptr})" end yield token_information_buf end end # GetTokenInformation buffer has been cleaned up by this point, nothing to return nil end module_function :get_token_information def parse_token_information_as_token_privileges(token_information_buf) raw_privileges = TOKEN_PRIVILEGES.new(token_information_buf) privileges = { :count => raw_privileges[:PrivilegeCount], :privileges => [] } offset = token_information_buf + TOKEN_PRIVILEGES.offset_of(:Privileges) privilege_ptr = FFI::Pointer.new(LUID_AND_ATTRIBUTES, offset) # extract each instance of LUID_AND_ATTRIBUTES 0.upto(privileges[:count] - 1) do |i| privileges[:privileges] << LUID_AND_ATTRIBUTES.new(privilege_ptr[i]) end privileges end module_function :parse_token_information_as_token_privileges def parse_token_information_as_token_elevation(token_information_buf) TOKEN_ELEVATION.new(token_information_buf) end module_function :parse_token_information_as_token_elevation TOKEN_ALL_ACCESS = 0xF01FF ERROR_NO_SUCH_PRIVILEGE = 1313 def process_privilege_symlink? privilege_symlink = false handle = get_current_process open_process_token(handle, TOKEN_ALL_ACCESS) do |token_handle| lookup_privilege_value('SeCreateSymbolicLinkPrivilege') do |luid| get_token_information(token_handle, :TokenPrivileges) do |token_info| token_privileges = parse_token_information_as_token_privileges(token_info) privilege_symlink = token_privileges[:privileges].any? { |p| p[:Luid].values == luid.values } end end end privilege_symlink 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? TOKEN_QUERY = 0x0008 # Returns whether or not the owner of the current process is running # with elevated security privileges. # # Only supported on Windows Vista or later. # def elevated_security? # default / pre-Vista elevated = false handle = nil begin handle = get_current_process open_process_token(handle, TOKEN_QUERY) do |token_handle| get_token_information(token_handle, :TokenElevation) do |token_info| token_elevation = parse_token_information_as_token_elevation(token_info) # TokenIsElevated member of the TOKEN_ELEVATION struct elevated = token_elevation[:TokenIsElevated] != 0 end end elevated rescue Puppet::Util::Windows::Error => e raise e if e.code != ERROR_NO_SUCH_PRIVILEGE ensure FFI::WIN32.CloseHandle(handle) if handle end end module_function :elevated_security? def windows_major_version ver = 0 FFI::MemoryPointer.new(OSVERSIONINFO.size) do |os_version_ptr| os_version = OSVERSIONINFO.new(os_version_ptr) os_version[:dwOSVersionInfoSize] = OSVERSIONINFO.size result = GetVersionExW(os_version_ptr) if result == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("GetVersionEx failed") end ver = os_version[:dwMajorVersion] end ver end module_function :windows_major_version # Returns a hash of the current environment variables encoded as UTF-8 # The memory block returned from GetEnvironmentStringsW is double-null terminated and the vars are paired as below; # Var1=Value1\0 # Var2=Value2\0 # VarX=ValueX\0\0 # Note - Some env variable names start with '=' and are excluded from the return value # Note - The env_ptr MUST be freed using the FreeEnvironmentStringsW function # Note - There is no technical limitation to the size of the environment block returned. # However a practical limit of 64K is used as no single environment variable can exceed 32KB def get_environment_strings env_ptr = GetEnvironmentStringsW() # pass :invalid => :replace to the Ruby String#encode to use replacement characters pairs = env_ptr.read_arbitrary_wide_string_up_to(65_534, :double_null, { :invalid => :replace }) .split(?\x00) .reject { |env_str| env_str.nil? || env_str.empty? || env_str[0] == '=' } .reject do |env_str| # reject any string containing the Unicode replacement character if env_str.include?("\uFFFD") Puppet.warning(_("Discarding environment variable %{string} which contains invalid bytes") % { string: env_str }) true end end .map { |env_pair| env_pair.split('=', 2) } pairs.to_h ensure if env_ptr && !env_ptr.null? if FreeEnvironmentStringsW(env_ptr) == FFI::WIN32_FALSE Puppet.debug "FreeEnvironmentStringsW memory leak" end end end module_function :get_environment_strings def set_environment_variable(name, val) raise Puppet::Util::Windows::Error(_('environment variable name must not be nil or empty')) if !name || name.empty? FFI::MemoryPointer.from_string_to_wide_string(name) do |name_ptr| if val.nil? if SetEnvironmentVariableW(name_ptr, FFI::MemoryPointer::NULL) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to remove environment variable: %{name}") % { name: name } end else FFI::MemoryPointer.from_string_to_wide_string(val) do |val_ptr| if SetEnvironmentVariableW(name_ptr, val_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to set environment variable: %{name}") % { name: name } end end end end end module_function :set_environment_variable def get_system_default_ui_language GetSystemDefaultUILanguage() end module_function :get_system_default_ui_language # Returns whether or not the OS has the ability to set elevated # token information. # # Returns true on Windows Vista or later, otherwise false # def supports_elevated_security? windows_major_version >= 6 end module_function :supports_elevated_security? end puppetlabs-puppet-789f600/lib/puppet/util/windows/registry.rb000066400000000000000000000404411470131746300244310ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/util/windows' module Puppet::Util::Windows module Registry require 'ffi' extend FFI::Library # https://msdn.microsoft.com/en-us/library/windows/desktop/aa384129(v=vs.85).aspx KEY64 = 0x100 KEY32 = 0x200 KEY_READ = 0x20019 KEY_WRITE = 0x20006 KEY_ALL_ACCESS = 0x2003f ERROR_NO_MORE_ITEMS = 259 WCHAR_SIZE = FFI.type_size(:wchar) def root(name) Win32::Registry.const_get(name) rescue NameError => e raise Puppet::Error, _("Invalid registry key '%{name}'") % { name: name }, e.backtrace end def open(name, path, mode = KEY_READ | KEY64, &block) hkey = root(name) begin hkey.open(path, mode) do |subkey| return yield subkey end rescue Win32::Registry::Error => error raise Puppet::Util::Windows::Error.new(_("Failed to open registry key '%{key}\\%{path}'") % { key: hkey.keyname, path: path }, error.code, error) end end def keys(key) keys = {} each_key(key) { |subkey, filetime| keys[subkey] = filetime } keys end # subkey is String which contains name of subkey. # wtime is last write time as FILETIME (64-bit integer). (see Registry.wtime2time) def each_key(key, &block) index = 0 subkey = nil subkey_max_len, _ = reg_query_info_key_max_lengths(key) loop do subkey, filetime = reg_enum_key(key, index, subkey_max_len) yield subkey, filetime unless subkey.nil? index += 1 break if subkey.nil? end index end def delete_key(key, subkey_name, mode = KEY64) reg_delete_key_ex(key, subkey_name, mode) end def values(key) vals = {} each_value(key) { |subkey, _type, data| vals[subkey] = data } vals end # Retrieve a set of values from a registry key given their names # Value names listed but not found in the registry will not be added to the # resultant Hashtable # # @param key [RegistryKey] An open handle to a Registry Key # @param names [String[]] An array of names of registry values to return if they exist # @return [Hashtable] A hashtable of all of the found values in the registry key def values_by_name(key, names) vals = {} names.each do |name| FFI::Pointer.from_string_to_wide_string(name) do |subkeyname_ptr| _, vals[name] = read(key, subkeyname_ptr) rescue Puppet::Util::Windows::Error => e # ignore missing names, but raise other errors raise e unless e.code == Puppet::Util::Windows::Error::ERROR_FILE_NOT_FOUND end end vals end def each_value(key, &block) index = 0 subkey = nil _, value_max_len = reg_query_info_key_max_lengths(key) loop do subkey, type, data = reg_enum_value(key, index, value_max_len) yield subkey, type, data unless subkey.nil? index += 1 break if subkey.nil? end index end def delete_value(key, subkey_name) reg_delete_value(key, subkey_name) end private # max number of wide characters including NULL terminator MAX_KEY_CHAR_LENGTH = 255 + 1 def reg_enum_key(key, index, max_key_char_length = MAX_KEY_CHAR_LENGTH) subkey = nil filetime = nil FFI::MemoryPointer.new(:dword) do |subkey_length_ptr| FFI::MemoryPointer.new(FFI::WIN32::FILETIME.size) do |filetime_ptr| FFI::MemoryPointer.new(:wchar, max_key_char_length) do |subkey_ptr| subkey_length_ptr.write_dword(max_key_char_length) # RegEnumKeyEx cannot be called twice to properly size the buffer result = RegEnumKeyExW(key.hkey, index, subkey_ptr, subkey_length_ptr, FFI::Pointer::NULL, FFI::Pointer::NULL, FFI::Pointer::NULL, filetime_ptr) break if result == ERROR_NO_MORE_ITEMS if result != FFI::ERROR_SUCCESS msg = _("Failed to enumerate %{key} registry keys at index %{index}") % { key: key.keyname, index: index } raise Puppet::Util::Windows::Error.new(msg, result) end filetime = FFI::WIN32::FILETIME.new(filetime_ptr) subkey_length = subkey_length_ptr.read_dword subkey = subkey_ptr.read_wide_string(subkey_length) end end end [subkey, filetime] end # max number of wide characters including NULL terminator MAX_VALUE_CHAR_LENGTH = 16_383 + 1 def reg_enum_value(key, index, max_value_length = MAX_VALUE_CHAR_LENGTH) subkey = nil type = nil data = nil FFI::MemoryPointer.new(:dword) do |subkey_length_ptr| FFI::MemoryPointer.new(:wchar, max_value_length) do |subkey_ptr| # RegEnumValueW cannot be called twice to properly size the buffer subkey_length_ptr.write_dword(max_value_length) result = RegEnumValueW(key.hkey, index, subkey_ptr, subkey_length_ptr, FFI::Pointer::NULL, FFI::Pointer::NULL, FFI::Pointer::NULL, FFI::Pointer::NULL) break if result == ERROR_NO_MORE_ITEMS if result != FFI::ERROR_SUCCESS msg = _("Failed to enumerate %{key} registry values at index %{index}") % { key: key.keyname, index: index } raise Puppet::Util::Windows::Error.new(msg, result) end subkey_length = subkey_length_ptr.read_dword subkey = subkey_ptr.read_wide_string(subkey_length) type, data = read(key, subkey_ptr) end end [subkey, type, data] end def reg_query_info_key_max_lengths(key) result = nil FFI::MemoryPointer.new(:dword) do |max_subkey_name_length_ptr| FFI::MemoryPointer.new(:dword) do |max_value_name_length_ptr| status = RegQueryInfoKeyW(key.hkey, FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL, max_subkey_name_length_ptr, FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL, max_value_name_length_ptr, FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL) if status != FFI::ERROR_SUCCESS msg = _("Failed to query registry %{key} for sizes") % { key: key.keyname } raise Puppet::Util::Windows::Error.new(msg, status) end result = [ # Unicode characters *not* including trailing NULL max_subkey_name_length_ptr.read_dword + 1, max_value_name_length_ptr.read_dword + 1 ] end end result end # Read a registry value named name and return array of # [ type, data ]. # When name is nil, the `default' value is read. # type is value type. (see Win32::Registry::Constants module) # data is value data, its class is: # :REG_SZ, REG_EXPAND_SZ # String # :REG_MULTI_SZ # Array of String # :REG_DWORD, REG_DWORD_BIG_ENDIAN, REG_QWORD # Integer # :REG_BINARY # String (contains binary data) # # When rtype is specified, the value type must be included by # rtype array, or TypeError is raised. def read(key, name_ptr, *rtype) result = nil query_value_ex(key, name_ptr) do |type, data_ptr, byte_length| unless rtype.empty? or rtype.include?(type) raise TypeError, _("Type mismatch (expect %{rtype} but %{type} present)") % { rtype: rtype.inspect, type: type } end string_length = 0 # buffer is raw bytes, *not* chars - less a NULL terminator string_length = (byte_length / WCHAR_SIZE) - 1 if byte_length > 0 begin result = case type when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ [type, data_ptr.read_wide_string(string_length, Encoding::UTF_8, true)] when Win32::Registry::REG_MULTI_SZ [type, data_ptr.read_wide_string(string_length).split(/\0/)] when Win32::Registry::REG_BINARY [type, data_ptr.read_bytes(byte_length)] when Win32::Registry::REG_DWORD [type, data_ptr.read_dword] when Win32::Registry::REG_DWORD_BIG_ENDIAN [type, data_ptr.order(:big).read_dword] when Win32::Registry::REG_QWORD [type, data_ptr.read_qword] else raise TypeError, _("Type %{type} is not supported.") % { type: type } end rescue IndexError => ex raise if ex.message !~ /^Memory access .* is out of bounds$/i parent_key_name = key.parent ? "#{key.parent.keyname}\\" : "" Puppet.warning _("A value in the registry key %{parent_key_name}%{key} is corrupt or invalid") % { parent_key_name: parent_key_name, key: key.keyname } end end result end def query_value_ex(key, name_ptr, &block) FFI::MemoryPointer.new(:dword) do |type_ptr| FFI::MemoryPointer.new(:dword) do |length_ptr| result = RegQueryValueExW(key.hkey, name_ptr, FFI::Pointer::NULL, type_ptr, FFI::Pointer::NULL, length_ptr) # The call to RegQueryValueExW below is potentially unsafe: # https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexw # # "If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, # the string may not have been stored with the proper terminating # null characters. Therefore, even if the function returns # ERROR_SUCCESS, the application should ensure that the string is # properly terminated before using it; otherwise, it may overwrite a # buffer. (Note that REG_MULTI_SZ strings should have two # terminating null characters.)" # # Since we don't know if the values will be properly null terminated, # extend the buffer to guarantee we can append one or two wide null # characters, without overwriting any data. base_bytes_len = length_ptr.read_dword pad_bytes_len = case type_ptr.read_dword when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ WCHAR_SIZE when Win32::Registry::REG_MULTI_SZ WCHAR_SIZE * 2 else 0 end FFI::MemoryPointer.new(:byte, base_bytes_len + pad_bytes_len) do |buffer_ptr| result = RegQueryValueExW(key.hkey, name_ptr, FFI::Pointer::NULL, type_ptr, buffer_ptr, length_ptr) # Ensure buffer is null terminated with 1 or 2 wchar nulls, depending on the type if result == FFI::ERROR_SUCCESS case type_ptr.read_dword when Win32::Registry::REG_SZ, Win32::Registry::REG_EXPAND_SZ buffer_ptr.put_uint16(base_bytes_len, 0) when Win32::Registry::REG_MULTI_SZ buffer_ptr.put_uint16(base_bytes_len, 0) buffer_ptr.put_uint16(base_bytes_len + WCHAR_SIZE, 0) end else # buffer is raw bytes, *not* chars - less a NULL terminator name_length = (name_ptr.size / WCHAR_SIZE) - 1 if name_ptr.size > 0 msg = _("Failed to read registry value %{value} at %{key}") % { value: name_ptr.read_wide_string(name_length), key: key.keyname } raise Puppet::Util::Windows::Error.new(msg, result) end # allows caller to use FFI MemoryPointer helpers to read / shape yield [type_ptr.read_dword, buffer_ptr, length_ptr.read_dword] end end end end def reg_delete_value(key, name) result = 0 FFI::Pointer.from_string_to_wide_string(name) do |name_ptr| result = RegDeleteValueW(key.hkey, name_ptr) if result != FFI::ERROR_SUCCESS msg = _("Failed to delete registry value %{name} at %{key}") % { name: name, key: key.keyname } raise Puppet::Util::Windows::Error.new(msg, result) end end result end def reg_delete_key_ex(key, name, regsam = KEY64) result = 0 FFI::Pointer.from_string_to_wide_string(name) do |name_ptr| result = RegDeleteKeyExW(key.hkey, name_ptr, regsam, 0) if result != FFI::ERROR_SUCCESS msg = _("Failed to delete registry key %{name} at %{key}") % { name: name, key: key.keyname } raise Puppet::Util::Windows::Error.new(msg, result) end end result end ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724862(v=vs.85).aspx # LONG WINAPI RegEnumKeyEx( # _In_ HKEY hKey, # _In_ DWORD dwIndex, # _Out_ LPTSTR lpName, # _Inout_ LPDWORD lpcName, # _Reserved_ LPDWORD lpReserved, # _Inout_ LPTSTR lpClass, # _Inout_opt_ LPDWORD lpcClass, # _Out_opt_ PFILETIME lpftLastWriteTime # ); ffi_lib :advapi32 attach_function_private :RegEnumKeyExW, [:handle, :dword, :lpwstr, :lpdword, :lpdword, :lpwstr, :lpdword, :pointer], :win32_long # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724865(v=vs.85).aspx # LONG WINAPI RegEnumValue( # _In_ HKEY hKey, # _In_ DWORD dwIndex, # _Out_ LPTSTR lpValueName, # _Inout_ LPDWORD lpcchValueName, # _Reserved_ LPDWORD lpReserved, # _Out_opt_ LPDWORD lpType, # _Out_opt_ LPBYTE lpData, # _Inout_opt_ LPDWORD lpcbData # ); ffi_lib :advapi32 attach_function_private :RegEnumValueW, [:handle, :dword, :lpwstr, :lpdword, :lpdword, :lpdword, :lpbyte, :lpdword], :win32_long # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724911(v=vs.85).aspx # LONG WINAPI RegQueryValueExW( # _In_ HKEY hKey, # _In_opt_ LPCTSTR lpValueName, # _Reserved_ LPDWORD lpReserved, # _Out_opt_ LPDWORD lpType, # _Out_opt_ LPBYTE lpData, # _Inout_opt_ LPDWORD lpcbData # ); ffi_lib :advapi32 attach_function_private :RegQueryValueExW, [:handle, :lpcwstr, :lpdword, :lpdword, :lpbyte, :lpdword], :win32_long # LONG WINAPI RegDeleteValue( # _In_ HKEY hKey, # _In_opt_ LPCTSTR lpValueName # ); ffi_lib :advapi32 attach_function_private :RegDeleteValueW, [:handle, :lpcwstr], :win32_long # LONG WINAPI RegDeleteKeyEx( # _In_ HKEY hKey, # _In_ LPCTSTR lpSubKey, # _In_ REGSAM samDesired, # _Reserved_ DWORD Reserved # ); ffi_lib :advapi32 attach_function_private :RegDeleteKeyExW, [:handle, :lpcwstr, :win32_ulong, :dword], :win32_long # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724902(v=vs.85).aspx # LONG WINAPI RegQueryInfoKey( # _In_ HKEY hKey, # _Out_opt_ LPTSTR lpClass, # _Inout_opt_ LPDWORD lpcClass, # _Reserved_ LPDWORD lpReserved, # _Out_opt_ LPDWORD lpcSubKeys, # _Out_opt_ LPDWORD lpcMaxSubKeyLen, # _Out_opt_ LPDWORD lpcMaxClassLen, # _Out_opt_ LPDWORD lpcValues, # _Out_opt_ LPDWORD lpcMaxValueNameLen, # _Out_opt_ LPDWORD lpcMaxValueLen, # _Out_opt_ LPDWORD lpcbSecurityDescriptor, # _Out_opt_ PFILETIME lpftLastWriteTime # ); ffi_lib :advapi32 attach_function_private :RegQueryInfoKeyW, [:handle, :lpwstr, :lpdword, :lpdword, :lpdword, :lpdword, :lpdword, :lpdword, :lpdword, :lpdword, :lpdword, :pointer], :win32_long end end puppetlabs-puppet-789f600/lib/puppet/util/windows/root_certs.rb000066400000000000000000000062511470131746300247450ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/util/windows' require_relative '../../../puppet/ssl/openssl_loader' require 'ffi' # Represents a collection of trusted root certificates. # # @api public class Puppet::Util::Windows::RootCerts include Enumerable extend FFI::Library def initialize(roots) @roots = roots end # Enumerates each root certificate. # @yieldparam cert [OpenSSL::X509::Certificate] each root certificate # @api public def each @roots.each { |cert| yield cert } end # Returns a new instance. # @return [Puppet::Util::Windows::RootCerts] object constructed from current root certificates def self.instance new(load_certs) end # Returns an array of root certificates. # # @return [Array<[OpenSSL::X509::Certificate]>] an array of root certificates # @api private def self.load_certs certs = [] # This is based on a patch submitted to openssl: # https://www.mail-archive.com/openssl-dev@openssl.org/msg26958.html ptr = FFI::Pointer::NULL store = CertOpenSystemStoreA(nil, "ROOT") begin while (ptr = CertEnumCertificatesInStore(store, ptr)) and !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 Puppet.warning(_("Failed to import root certificate: %{detail}") % { detail: detail.inspect }) end end ensure CertCloseStore(store, 0) end certs end ffi_convention :stdcall # typedef void *HCERTSTORE; # https://msdn.microsoft.com/en-us/library/windows/desktop/aa377189(v=vs.85).aspx # typedef struct _CERT_CONTEXT { # DWORD dwCertEncodingType; # BYTE *pbCertEncoded; # DWORD cbCertEncoded; # PCERT_INFO pCertInfo; # HCERTSTORE hCertStore; # } CERT_CONTEXT, *PCERT_CONTEXT;typedef const CERT_CONTEXT *PCCERT_CONTEXT; class CERT_CONTEXT < FFI::Struct layout( :dwCertEncodingType, :dword, :pbCertEncoded, :pointer, :cbCertEncoded, :dword, :pCertInfo, :pointer, :hCertStore, :handle ) end # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376560(v=vs.85).aspx # HCERTSTORE # WINAPI # CertOpenSystemStoreA( # __in_opt HCRYPTPROV_LEGACY hProv, # __in LPCSTR szSubsystemProtocol # ); # typedef ULONG_PTR HCRYPTPROV_LEGACY; ffi_lib :crypt32 attach_function_private :CertOpenSystemStoreA, [:ulong_ptr, :lpcstr], :handle # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376050(v=vs.85).aspx # PCCERT_CONTEXT # WINAPI # CertEnumCertificatesInStore( # __in HCERTSTORE hCertStore, # __in_opt PCCERT_CONTEXT pPrevCertContext # ); ffi_lib :crypt32 attach_function_private :CertEnumCertificatesInStore, [:handle, :pointer], :pointer # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376026(v=vs.85).aspx # BOOL # WINAPI # CertCloseStore( # __in_opt HCERTSTORE hCertStore, # __in DWORD dwFlags # ); ffi_lib :crypt32 attach_function_private :CertCloseStore, [:handle, :dword], :win32_bool end puppetlabs-puppet-789f600/lib/puppet/util/windows/security.rb000066400000000000000000001114241470131746300244300ustar00rootroot00000000000000# frozen_string_literal: true # This class maps POSIX owner, group, and modes to the Windows # security model, and back. # # The primary goal of this mapping is to ensure that owner, group, and # modes can be round-tripped in a consistent and deterministic # way. Otherwise, Puppet might think file resources are out-of-sync # every time it runs. A secondary goal is to provide equivalent # permissions for common use-cases. For example, setting the owner to # "Administrators", group to "Users", and mode to 750 (which also # denies access to everyone else. # # There are some well-known problems mapping windows and POSIX # permissions due to differences between the two security # models. Search for "POSIX permission mapping leak". In POSIX, access # to a file is determined solely based on the most specific class # (user, group, other). So a mode of 460 would deny write access to # the owner even if they are a member of the group. But in Windows, # the entire access control list is walked until the user is # explicitly denied or allowed (denied take precedence, and if neither # occurs they are denied). As a result, a user could be allowed access # based on their group membership. To solve this problem, other people # have used deny access control entries to more closely model POSIX, # but this introduces a lot of complexity. # # In general, this implementation only supports "typical" permissions, # where group permissions are a subset of user, and other permissions # are a subset of group, e.g. 754, but not 467. However, there are # some Windows quirks to be aware of. # # * The owner can be either a user or group SID, and most system files # are owned by the Administrators group. # * The group can be either a user or group SID. # * Unexpected results can occur if the owner and group are the # same, but the user and group classes are different, e.g. 750. In # this case, it is not possible to allow write access to the owner, # but not the group. As a result, the actual permissions set on the # file would be 770. # * In general, only privileged users can set the owner, group, or # change the mode for files they do not own. In 2003, the user must # be a member of the Administrators group. In Vista/2008, the user # must be running with elevated privileges. # * A file/dir can be deleted by anyone with the DELETE access right # OR by anyone that has the FILE_DELETE_CHILD access right for the # parent. See https://support.microsoft.com/kb/238018. But on Unix, # the user must have write access to the file/dir AND execute access # to all of the parent path components. # * Many access control entries are inherited from parent directories, # and it is common for file/dirs to have more than 3 entries, # e.g. Users, Power Users, Administrators, SYSTEM, etc, which cannot # be mapped into the 3 class POSIX model. The get_mode method will # set the S_IEXTRA bit flag indicating that an access control entry # was found whose SID is neither the owner, group, or other. This # 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 # automatically rebuild the access control list based on the new # (and different) owner or group. require_relative '../../../puppet/util/windows' require 'pathname' require 'ffi' module Puppet::Util::Windows::Security include Puppet::Util::Windows::String extend Puppet::Util::Windows::Security extend FFI::Library # file modes S_IRUSR = 0o000400 S_IRGRP = 0o000040 S_IROTH = 0o000004 S_IWUSR = 0o000200 S_IWGRP = 0o000020 S_IWOTH = 0o000002 S_IXUSR = 0o000100 S_IXGRP = 0o000010 S_IXOTH = 0o000001 S_IRWXU = 0o000700 S_IRWXG = 0o000070 S_IRWXO = 0o000007 S_ISVTX = 0o001000 S_IEXTRA = 0o2000000 # represents an extra ace S_ISYSTEM_MISSING = 0o4000000 # constants that are missing from Windows::Security PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000 UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000 NO_INHERITANCE = 0x0 SE_DACL_PROTECTED = 0x1000 FILE = Puppet::Util::Windows::File SE_BACKUP_NAME = 'SeBackupPrivilege' SE_DEBUG_NAME = 'SeDebugPrivilege' SE_RESTORE_NAME = 'SeRestorePrivilege' DELETE = 0x00010000 READ_CONTROL = 0x20000 WRITE_DAC = 0x40000 WRITE_OWNER = 0x80000 OWNER_SECURITY_INFORMATION = 1 GROUP_SECURITY_INFORMATION = 2 DACL_SECURITY_INFORMATION = 4 # 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" # and can either be a user or group. Only a user with the # 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) sd = get_security_descriptor(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 # value is a SID string, e.g. "S-1-5-32-544". Any user with read # access to an object can get the owner. Only a user with the # SE_BACKUP_NAME privilege in their process token can get the owner # for objects they do not have read access to. def get_owner(path) return unless supports_acl?(path) get_security_descriptor(path).owner end # Set the owner of the object referenced by +path+ to the specified # +group_sid+. The group sid should be of the form "S-1-5-32-544" # and can either be a user or group. Any user with WRITE_OWNER # 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) sd = get_security_descriptor(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 # value is a SID string, e.g. "S-1-5-32-544". Any user with read # access to an object can get the group. Only a user with the # SE_BACKUP_NAME privilege in their process token can get the group # for objects they do not have read access to. def get_group(path) return unless supports_acl?(path) get_security_descriptor(path).group end FILE_PERSISTENT_ACLS = 0x00000008 def supports_acl?(path) supported = false root = Pathname.new(path).enum_for(:ascend).to_a.last.to_s # 'A trailing backslash is required' root = "#{root}\\" unless root =~ %r{[/\\]$} FFI::MemoryPointer.new(:pointer, 1) do |flags_ptr| if GetVolumeInformationW(wide_string(root), FFI::Pointer::NULL, 0, FFI::Pointer::NULL, FFI::Pointer::NULL, flags_ptr, FFI::Pointer::NULL, 0) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to get volume information") end supported = flags_ptr.read_dword & FILE_PERSISTENT_ACLS == FILE_PERSISTENT_ACLS end supported end MASK_TO_MODE = { FILE::FILE_GENERIC_READ => S_IROTH, FILE::FILE_GENERIC_WRITE => S_IWOTH, (FILE::FILE_GENERIC_EXECUTE & ~FILE::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 # with read access to an object can get the mode. Only a user with # the SE_BACKUP_NAME privilege in their process token can get the # mode for objects they do not have read access to. def get_mode(path) return unless supports_acl?(path) well_known_world_sid = Puppet::Util::Windows::SID::Everyone well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem well_known_app_packages_sid = Puppet::Util::Windows::SID::AllAppPackages mode = S_ISYSTEM_MISSING 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::FILE_WRITE_DATA | FILE::FILE_EXECUTE | FILE::FILE_DELETE_CHILD)) == (FILE::FILE_WRITE_DATA | FILE::FILE_EXECUTE) mode |= S_ISVTX; end when well_known_nobody_sid if (ace.mask & FILE::FILE_APPEND_DATA).nonzero? mode |= S_ISVTX end when well_known_app_packages_sid, well_known_system_sid # do nothing else # puts "Warning, unable to map SID into POSIX mode: #{ace.sid}" mode |= S_IEXTRA end 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 = { S_IROTH => FILE::FILE_GENERIC_READ, S_IWOTH => FILE::FILE_GENERIC_WRITE, S_IXOTH => (FILE::FILE_GENERIC_EXECUTE & ~FILE::FILE_READ_ATTRIBUTES), } # Set the mode of the object referenced by +path+ to the specified # +mode+. The mode should be specified as POSIX-style read, write, # and execute modes for the user, group, and other classes, # e.g. 0640. The sticky bit, S_ISVTX, is supported, but is only # meaningful for directories. If set, group and others are not # allowed to delete child objects for which they are not the owner. # By default, the DACL is set to protected, meaning it does not # inherit access control entries from parent objects. This can be # changed by setting +protected+ to false. The owner of the object # (with READ_CONTROL and WRITE_DACL access) can always change the # mode. Only a user with the SE_BACKUP_NAME and SE_RESTORE_NAME # 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, managing_owner = false, managing_group = false) sd = get_security_descriptor(path) well_known_world_sid = Puppet::Util::Windows::SID::Everyone well_known_nobody_sid = Puppet::Util::Windows::SID::Nobody well_known_system_sid = Puppet::Util::Windows::SID::LocalSystem owner_allow = FILE::STANDARD_RIGHTS_ALL | FILE::FILE_READ_ATTRIBUTES | FILE::FILE_WRITE_ATTRIBUTES # this prevents a mode that is not 7 from taking ownership of a file based # on group membership and rewriting it / making it executable group_allow = FILE::STANDARD_RIGHTS_READ | FILE::FILE_READ_ATTRIBUTES | FILE::SYNCHRONIZE other_allow = FILE::STANDARD_RIGHTS_READ | FILE::FILE_READ_ATTRIBUTES | FILE::SYNCHRONIZE nobody_allow = 0 system_allow = 0 MODE_TO_MASK.each do |k, v| if ((mode >> 6) & k) == k owner_allow |= v end if ((mode >> 3) & k) == k group_allow |= v end if (mode & k) == k other_allow |= v end end # With a mode value of '7' for group / other, the value must then include # additional perms beyond STANDARD_RIGHTS_READ to allow DACL modification if (mode & S_IRWXG) == S_IRWXG group_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER end if (mode & S_IRWXO) == S_IRWXO other_allow |= FILE::DELETE | FILE::WRITE_DAC | FILE::WRITE_OWNER end if (mode & S_ISVTX).nonzero? nobody_allow |= FILE::FILE_APPEND_DATA; end isownergroup = sd.owner == sd.group # 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::FILE_ALL_ACCESS else # It is possible to set SYSTEM with a mode other than Full Control (7) however this makes no sense and in practical terms # should not be done. We can trap these instances and correct them before being applied. if (sd.owner == well_known_system_sid) && (owner_allow != FILE::FILE_ALL_ACCESS) # If owner and group are both SYSTEM but group is unmanaged the control rights of system will be set to FullControl by # the unmanaged group, so there is no need for the warning if managing_owner && (!isownergroup || managing_group) # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated Puppet.warning _("Setting control rights for %{path} owner SYSTEM to less than Full Control rights. Setting SYSTEM rights to less than Full Control may have unintented consequences for operations on this file") % { path: path } elsif managing_owner && isownergroup # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated Puppet.debug { _("%{path} owner and group both set to user SYSTEM, but group is not managed directly: SYSTEM user rights will be set to FullControl by group") % { path: path } } else # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated Puppet.debug { _("An attempt to set mode %{mode} on item %{path} would result in the owner, SYSTEM, to have less than Full Control rights. This attempt has been corrected to Full Control") % { mode: mode.to_s(8), path: path } } owner_allow = FILE::FILE_ALL_ACCESS end end if (sd.group == well_known_system_sid) && (group_allow != FILE::FILE_ALL_ACCESS) # If owner and group are both SYSTEM but owner is unmanaged the control rights of system will be set to FullControl by # the unmanaged owner, so there is no need for the warning. if managing_group && (!isownergroup || managing_owner) # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated Puppet.warning _("Setting control rights for %{path} group SYSTEM to less than Full Control rights. Setting SYSTEM rights to less than Full Control may have unintented consequences for operations on this file") % { path: path } elsif managing_group && isownergroup # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated Puppet.debug { _("%{path} owner and group both set to user SYSTEM, but owner is not managed directly: SYSTEM user rights will be set to FullControl by owner") % { path: path } } else # TRANSLATORS 'SYSTEM' is a Windows name and should not be translated Puppet.debug { _("An attempt to set mode %{mode} on item %{path} would result in the group, SYSTEM, to have less than Full Control rights. This attempt has been corrected to Full Control") % { mode: mode.to_s(8), path: path } } group_allow = FILE::FILE_ALL_ACCESS end end end # even though FILE_DELETE_CHILD only applies to directories, it can be set on files # this is necessary to do to ensure a file ends up with (F) FullControl if (mode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR) owner_allow |= FILE::FILE_DELETE_CHILD end if (mode & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) && (mode & S_ISVTX) == 0 group_allow |= FILE::FILE_DELETE_CHILD end if (mode & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) && (mode & S_ISVTX) == 0 other_allow |= FILE::FILE_DELETE_CHILD end # if owner and group the same, then map group permissions to the one owner ACE if isownergroup owner_allow |= group_allow end # if any ACE allows write, then clear readonly bit, but do this before we overwrite # the DACl and lose our ability to set the attribute if ((owner_allow | group_allow | other_allow) & FILE::FILE_WRITE_DATA) == FILE::FILE_WRITE_DATA FILE.remove_attributes(path, FILE::FILE_ATTRIBUTE_READONLY) end isdir = File.directory?(path) 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) # TODO: system should be first? flags = !isdir ? 0 : Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE dacl.allow(well_known_system_sid, system_allow, flags) # add inherit-only aces for child dirs and files that are created within the dir inherit_only = Puppet::Util::Windows::AccessControlEntry::INHERIT_ONLY_ACE if isdir inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::CONTAINER_INHERIT_ACE dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow, inherit) dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow, inherit) inherit = inherit_only | Puppet::Util::Windows::AccessControlEntry::OBJECT_INHERIT_ACE # allow any previously set bits *except* for these perms_to_strip = ~(FILE::FILE_EXECUTE + FILE::WRITE_OWNER + FILE::WRITE_DAC) dacl.allow(Puppet::Util::Windows::SID::CreatorOwner, owner_allow & perms_to_strip, inherit) dacl.allow(Puppet::Util::Windows::SID::CreatorGroup, group_allow & perms_to_strip, inherit) end new_sd = Puppet::Util::Windows::SecurityDescriptor.new(sd.owner, sd.group, dacl, protected) set_security_descriptor(path, new_sd) nil end ACL_REVISION = 2 def add_access_allowed_ace(acl, mask, sid, inherit = nil) inherit ||= NO_INHERITANCE Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr| if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Invalid SID") end if AddAccessAllowedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to add access control entry") end end # ensure this method is void if it doesn't raise nil end def add_access_denied_ace(acl, mask, sid, inherit = nil) inherit ||= NO_INHERITANCE Puppet::Util::Windows::SID.string_to_sid_ptr(sid) do |sid_ptr| if Puppet::Util::Windows::SID.IsValidSid(sid_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Invalid SID") end if AddAccessDeniedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to add access control entry") end end # ensure this method is void if it doesn't raise nil end def parse_dacl(dacl_ptr) # REMIND: need to handle NULL DACL if IsValidAcl(dacl_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Invalid DACL") end dacl_struct = ACL.new(dacl_ptr) ace_count = dacl_struct[:AceCount] dacl = Puppet::Util::Windows::AccessControlList.new # deny all return dacl if ace_count == 0 0.upto(ace_count - 1) do |i| FFI::MemoryPointer.new(:pointer, 1) do |ace_ptr| next if GetAce(dacl_ptr, i, ace_ptr) == FFI::WIN32_FALSE # ACE structures vary depending on the type. We are only concerned with # ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACEs, which have the same layout ace = GENERIC_ACCESS_ACE.new(ace_ptr.get_pointer(0)) # deref LPVOID * ace_type = ace[:Header][:AceType] if ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE && ace_type != Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE Puppet.warning _("Unsupported access control entry type: 0x%{type}") % { type: ace_type.to_s(16) } next end # using pointer addition gives the FFI::Pointer a size, but that's OK here sid = Puppet::Util::Windows::SID.sid_ptr_to_string(ace.pointer + GENERIC_ACCESS_ACE.offset_of(:SidStart)) mask = ace[:Mask] ace_flags = ace[:Header][:AceFlags] case ace_type when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE dacl.allow(sid, mask, ace_flags) when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE dacl.deny(sid, mask, ace_flags) end end end dacl end # Open an existing file with the specified access mode, and execute a # block with the opened file HANDLE. def open_file(path, access, &block) handle = CreateFileW( wide_string(path), access, FILE::FILE_SHARE_READ | FILE::FILE_SHARE_WRITE, FFI::Pointer::NULL, # security_attributes FILE::OPEN_EXISTING, FILE::FILE_FLAG_OPEN_REPARSE_POINT | FILE::FILE_FLAG_BACKUP_SEMANTICS, FFI::Pointer::NULL_HANDLE ) # template if handle == Puppet::Util::Windows::File::INVALID_HANDLE_VALUE raise Puppet::Util::Windows::Error, _("Failed to open '%{path}'") % { path: path } end begin yield handle ensure FFI::WIN32.CloseHandle(handle) if handle end # handle has already had CloseHandle called against it, nothing to return nil end # Execute a block with the specified privilege enabled def with_privilege(privilege, &block) set_privilege(privilege, true) yield ensure set_privilege(privilege, false) end SE_PRIVILEGE_ENABLED = 0x00000002 TOKEN_ADJUST_PRIVILEGES = 0x0020 # Enable or disable a privilege. Note this doesn't add any privileges the # user doesn't already has, it just enables privileges that are disabled. def set_privilege(privilege, enable) return unless Puppet.features.root? Puppet::Util::Windows::Process.with_process_token(TOKEN_ADJUST_PRIVILEGES) do |token| Puppet::Util::Windows::Process.lookup_privilege_value(privilege) do |luid| FFI::MemoryPointer.new(Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.size) do |luid_and_attributes_ptr| # allocate unmanaged memory for structs that we clean up afterwards luid_and_attributes = Puppet::Util::Windows::Process::LUID_AND_ATTRIBUTES.new(luid_and_attributes_ptr) luid_and_attributes[:Luid] = luid luid_and_attributes[:Attributes] = enable ? SE_PRIVILEGE_ENABLED : 0 FFI::MemoryPointer.new(Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.size) do |token_privileges_ptr| token_privileges = Puppet::Util::Windows::Process::TOKEN_PRIVILEGES.new(token_privileges_ptr) token_privileges[:PrivilegeCount] = 1 token_privileges[:Privileges][0] = luid_and_attributes # size is correct given we only have 1 LUID, otherwise would be: # [:PrivilegeCount].size + [:PrivilegeCount] * LUID_AND_ATTRIBUTES.size if AdjustTokenPrivileges(token, FFI::WIN32_FALSE, token_privileges, token_privileges.size, FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to adjust process privileges") end end end end end # token / luid structs freed by this point, so return true as nothing raised true end def get_security_descriptor(path) sd = nil with_privilege(SE_BACKUP_NAME) do open_file(path, READ_CONTROL) do |handle| FFI::MemoryPointer.new(:pointer, 1) do |owner_sid_ptr_ptr| FFI::MemoryPointer.new(:pointer, 1) do |group_sid_ptr_ptr| FFI::MemoryPointer.new(:pointer, 1) do |dacl_ptr_ptr| FFI::MemoryPointer.new(:pointer, 1) do |sd_ptr_ptr| rv = GetSecurityInfo( handle, :SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, owner_sid_ptr_ptr, group_sid_ptr_ptr, dacl_ptr_ptr, FFI::Pointer::NULL, # sacl sd_ptr_ptr ) # sec desc raise Puppet::Util::Windows::Error, _("Failed to get security information") if rv != FFI::ERROR_SUCCESS # these 2 convenience params are not freed since they point inside sd_ptr owner = Puppet::Util::Windows::SID.sid_ptr_to_string(owner_sid_ptr_ptr.get_pointer(0)) group = Puppet::Util::Windows::SID.sid_ptr_to_string(group_sid_ptr_ptr.get_pointer(0)) FFI::MemoryPointer.new(:word, 1) do |control| FFI::MemoryPointer.new(:dword, 1) do |revision| sd_ptr_ptr.read_win32_local_pointer do |sd_ptr| if GetSecurityDescriptorControl(sd_ptr, control, revision) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to get security descriptor control") end protect = (control.read_word & SE_DACL_PROTECTED) == SE_DACL_PROTECTED dacl = parse_dacl(dacl_ptr_ptr.get_pointer(0)) sd = Puppet::Util::Windows::SecurityDescriptor.new(owner, group, dacl, protect) end end end end end end end end end sd end def get_max_generic_acl_size(ace_count) # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378853(v=vs.85).aspx # To calculate the initial size of an ACL, add the following together, and then align the result to the nearest DWORD: # * Size of the ACL structure. # * Size of each ACE structure that the ACL is to contain minus the SidStart member (DWORD) of the ACE. # * Length of the SID that each ACE is to contain. ACL.size + ace_count * MAXIMUM_GENERIC_ACE_SIZE 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) FFI::MemoryPointer.new(:byte, get_max_generic_acl_size(sd.dacl.count)) do |acl_ptr| if InitializeAcl(acl_ptr, acl_ptr.size, ACL_REVISION) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to initialize ACL") end if IsValidAcl(acl_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Invalid DACL") end with_privilege(SE_BACKUP_NAME) do with_privilege(SE_RESTORE_NAME) do open_file(path, READ_CONTROL | WRITE_DAC | WRITE_OWNER) do |handle| Puppet::Util::Windows::SID.string_to_sid_ptr(sd.owner) do |owner_sid_ptr| Puppet::Util::Windows::SID.string_to_sid_ptr(sd.group) do |group_sid_ptr| sd.dacl.each do |ace| case ace.type when Puppet::Util::Windows::AccessControlEntry::ACCESS_ALLOWED_ACE_TYPE # puts "ace: allow, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}" add_access_allowed_ace(acl_ptr, ace.mask, ace.sid, ace.flags) when Puppet::Util::Windows::AccessControlEntry::ACCESS_DENIED_ACE_TYPE # puts "ace: deny, sid #{Puppet::Util::Windows::SID.sid_to_name(ace.sid)}, mask 0x#{ace.mask.to_s(16)}" add_access_denied_ace(acl_ptr, ace.mask, ace.sid, ace.flags) 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, owner_sid_ptr, group_sid_ptr, acl_ptr, FFI::MemoryPointer::NULL) if rv != FFI::ERROR_SUCCESS raise Puppet::Util::Windows::Error, _("Failed to set security information") end end end end end end end end ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx # 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 # ); ffi_lib :kernel32 attach_function_private :CreateFileW, [:lpcwstr, :dword, :dword, :pointer, :dword, :dword, :handle], :handle # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364993(v=vs.85).aspx # BOOL WINAPI GetVolumeInformation( # _In_opt_ LPCTSTR lpRootPathName, # _Out_opt_ LPTSTR lpVolumeNameBuffer, # _In_ DWORD nVolumeNameSize, # _Out_opt_ LPDWORD lpVolumeSerialNumber, # _Out_opt_ LPDWORD lpMaximumComponentLength, # _Out_opt_ LPDWORD lpFileSystemFlags, # _Out_opt_ LPTSTR lpFileSystemNameBuffer, # _In_ DWORD nFileSystemNameSize # ); ffi_lib :kernel32 attach_function_private :GetVolumeInformationW, [:lpcwstr, :lpwstr, :dword, :lpdword, :lpdword, :lpdword, :lpwstr, :dword], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374951(v=vs.85).aspx # BOOL WINAPI AddAccessAllowedAceEx( # _Inout_ PACL pAcl, # _In_ DWORD dwAceRevision, # _In_ DWORD AceFlags, # _In_ DWORD AccessMask, # _In_ PSID pSid # ); ffi_lib :advapi32 attach_function_private :AddAccessAllowedAceEx, [:pointer, :dword, :dword, :dword, :pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374964(v=vs.85).aspx # BOOL WINAPI AddAccessDeniedAceEx( # _Inout_ PACL pAcl, # _In_ DWORD dwAceRevision, # _In_ DWORD AceFlags, # _In_ DWORD AccessMask, # _In_ PSID pSid # ); ffi_lib :advapi32 attach_function_private :AddAccessDeniedAceEx, [:pointer, :dword, :dword, :dword, :pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374931(v=vs.85).aspx # typedef struct _ACL { # BYTE AclRevision; # BYTE Sbz1; # WORD AclSize; # WORD AceCount; # WORD Sbz2; # } ACL, *PACL; class ACL < FFI::Struct layout :AclRevision, :byte, :Sbz1, :byte, :AclSize, :word, :AceCount, :word, :Sbz2, :word end # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374912(v=vs.85).aspx # ACE types # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374919(v=vs.85).aspx # typedef struct _ACE_HEADER { # BYTE AceType; # BYTE AceFlags; # WORD AceSize; # } ACE_HEADER, *PACE_HEADER; class ACE_HEADER < FFI::Struct layout :AceType, :byte, :AceFlags, :byte, :AceSize, :word end # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374892(v=vs.85).aspx # ACCESS_MASK # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374847(v=vs.85).aspx # typedef struct _ACCESS_ALLOWED_ACE { # ACE_HEADER Header; # ACCESS_MASK Mask; # DWORD SidStart; # } ACCESS_ALLOWED_ACE, *PACCESS_ALLOWED_ACE; # # https://msdn.microsoft.com/en-us/library/windows/desktop/aa374879(v=vs.85).aspx # typedef struct _ACCESS_DENIED_ACE { # ACE_HEADER Header; # ACCESS_MASK Mask; # DWORD SidStart; # } ACCESS_DENIED_ACE, *PACCESS_DENIED_ACE; class GENERIC_ACCESS_ACE < FFI::Struct # ACE structures must be aligned on DWORD boundaries. All Windows # memory-management functions return DWORD-aligned handles to memory pack 4 layout :Header, ACE_HEADER, :Mask, :dword, :SidStart, :dword end # https://stackoverflow.com/a/1792930 MAXIMUM_SID_BYTES_LENGTH = 68 MAXIMUM_GENERIC_ACE_SIZE = GENERIC_ACCESS_ACE.offset_of(:SidStart) + MAXIMUM_SID_BYTES_LENGTH # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446634(v=vs.85).aspx # BOOL WINAPI GetAce( # _In_ PACL pAcl, # _In_ DWORD dwAceIndex, # _Out_ LPVOID *pAce # ); ffi_lib :advapi32 attach_function_private :GetAce, [:pointer, :dword, :pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa375202(v=vs.85).aspx # BOOL WINAPI AdjustTokenPrivileges( # _In_ HANDLE TokenHandle, # _In_ BOOL DisableAllPrivileges, # _In_opt_ PTOKEN_PRIVILEGES NewState, # _In_ DWORD BufferLength, # _Out_opt_ PTOKEN_PRIVILEGES PreviousState, # _Out_opt_ PDWORD ReturnLength # ); ffi_lib :advapi32 attach_function_private :AdjustTokenPrivileges, [:handle, :win32_bool, :pointer, :dword, :pointer, :pdword], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/hardware/ff556610(v=vs.85).aspx # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379561(v=vs.85).aspx # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446647(v=vs.85).aspx # 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_private :GetSecurityDescriptorControl, [:pointer, :lpword, :lpdword], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378853(v=vs.85).aspx # BOOL WINAPI InitializeAcl( # _Out_ PACL pAcl, # _In_ DWORD nAclLength, # _In_ DWORD dwAclRevision # ); ffi_lib :advapi32 attach_function_private :InitializeAcl, [:pointer, :dword, :dword], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379142(v=vs.85).aspx # BOOL WINAPI IsValidAcl( # _In_ PACL pAcl # ); ffi_lib :advapi32 attach_function_private :IsValidAcl, [:pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379593(v=vs.85).aspx SE_OBJECT_TYPE = enum( :SE_UNKNOWN_OBJECT_TYPE, 0, :SE_FILE_OBJECT, :SE_SERVICE, :SE_PRINTER, :SE_REGISTRY_KEY, :SE_LMSHARE, :SE_KERNEL_OBJECT, :SE_WINDOW_OBJECT, :SE_DS_OBJECT, :SE_DS_OBJECT_ALL, :SE_PROVIDER_DEFINED_OBJECT, :SE_WMIGUID_OBJECT, :SE_REGISTRY_WOW64_32KEY ) # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446654(v=vs.85).aspx # DWORD WINAPI GetSecurityInfo( # _In_ HANDLE handle, # _In_ SE_OBJECT_TYPE ObjectType, # _In_ SECURITY_INFORMATION SecurityInfo, # _Out_opt_ PSID *ppsidOwner, # _Out_opt_ PSID *ppsidGroup, # _Out_opt_ PACL *ppDacl, # _Out_opt_ PACL *ppSacl, # _Out_opt_ PSECURITY_DESCRIPTOR *ppSecurityDescriptor # ); ffi_lib :advapi32 attach_function_private :GetSecurityInfo, [:handle, SE_OBJECT_TYPE, :dword, :pointer, :pointer, :pointer, :pointer, :pointer], :dword # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379588(v=vs.85).aspx # DWORD WINAPI SetSecurityInfo( # _In_ HANDLE handle, # _In_ SE_OBJECT_TYPE ObjectType, # _In_ SECURITY_INFORMATION SecurityInfo, # _In_opt_ PSID psidOwner, # _In_opt_ PSID psidGroup, # _In_opt_ PACL pDacl, # _In_opt_ PACL pSacl # ); ffi_lib :advapi32 # TODO: SECURITY_INFORMATION is actually a bitmask the size of a DWORD attach_function_private :SetSecurityInfo, [:handle, SE_OBJECT_TYPE, :dword, :pointer, :pointer, :pointer, :pointer], :dword end puppetlabs-puppet-789f600/lib/puppet/util/windows/security_descriptor.rb000066400000000000000000000037551470131746300266750ustar00rootroot00000000000000# frozen_string_literal: true # 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 https://msdn.microsoft.com/en-us/library/windows/desktop/aa379563(v=vs.85).aspx # @api private class Puppet::Util::Windows::SecurityDescriptor require_relative '../../../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 puppetlabs-puppet-789f600/lib/puppet/util/windows/service.rb000066400000000000000000000760001470131746300242210ustar00rootroot00000000000000# coding: utf-8 # frozen_string_literal: true require_relative '../../../puppet/ffi/windows' module Puppet::Util::Windows # This module is designed to provide an API between the windows system and puppet for # service management. # # for an overview of the service state transitions see: https://docs.microsoft.com/en-us/windows/desktop/Services/service-status-transitions module Service extend Puppet::Util::Windows::String include Puppet::FFI::Windows::Constants extend Puppet::FFI::Windows::Constants include Puppet::FFI::Windows::Structs extend Puppet::FFI::Windows::Structs include Puppet::FFI::Windows::Functions extend Puppet::FFI::Windows::Functions # Returns true if the service exists, false otherwise. # # @param [String] service_name name of the service def exists?(service_name) open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS) do |_| true end rescue Puppet::Util::Windows::Error => e return false if e.code == ERROR_SERVICE_DOES_NOT_EXIST raise e end module_function :exists? # Start a windows service # # @param [String] service_name name of the service to start # @param optional [Integer] timeout the minumum number of seconds to wait before timing out def start(service_name, timeout: DEFAULT_TIMEOUT) Puppet.debug _("Starting the %{service_name} service. Timeout set to: %{timeout} seconds") % { service_name: service_name, timeout: timeout } valid_initial_states = [ SERVICE_STOP_PENDING, SERVICE_STOPPED, SERVICE_START_PENDING ] transition_service_state(service_name, valid_initial_states, SERVICE_RUNNING, timeout) do |service| if StartServiceW(service, 0, FFI::Pointer::NULL) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to start the service") end end Puppet.debug _("Successfully started the %{service_name} service") % { service_name: service_name } end module_function :start # Stop a windows service # # @param [String] service_name name of the service to stop # @param optional [Integer] timeout the minumum number of seconds to wait before timing out def stop(service_name, timeout: DEFAULT_TIMEOUT) Puppet.debug _("Stopping the %{service_name} service. Timeout set to: %{timeout} seconds") % { service_name: service_name, timeout: timeout } valid_initial_states = SERVICE_STATES.keys - [SERVICE_STOPPED] transition_service_state(service_name, valid_initial_states, SERVICE_STOPPED, timeout) do |service| send_service_control_signal(service, SERVICE_CONTROL_STOP) end Puppet.debug _("Successfully stopped the %{service_name} service") % { service_name: service_name } end module_function :stop # Resume a paused windows service # # @param [String] service_name name of the service to resume # @param optional [Integer] :timeout the minumum number of seconds to wait before timing out def resume(service_name, timeout: DEFAULT_TIMEOUT) Puppet.debug _("Resuming the %{service_name} service. Timeout set to: %{timeout} seconds") % { service_name: service_name, timeout: timeout } valid_initial_states = [ SERVICE_PAUSE_PENDING, SERVICE_PAUSED, SERVICE_CONTINUE_PENDING ] transition_service_state(service_name, valid_initial_states, SERVICE_RUNNING, timeout) do |service| # The SERVICE_CONTROL_CONTINUE signal can only be sent when # the service is in the SERVICE_PAUSED state wait_on_pending_state(service, SERVICE_PAUSE_PENDING, timeout) send_service_control_signal(service, SERVICE_CONTROL_CONTINUE) end Puppet.debug _("Successfully resumed the %{service_name} service") % { service_name: service_name } end module_function :resume # Query the state of a service using QueryServiceStatusEx # # @param [string] service_name name of the service to query # @return [string] the status of the service def service_state(service_name) state = nil open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS) do |service| query_status(service) do |status| state = SERVICE_STATES[status[:dwCurrentState]] end end if state.nil? raise Puppet::Error, _("Unknown Service state '%{current_state}' for '%{service_name}'") % { current_state: state.to_s, service_name: service_name } end state end module_function :service_state # Query the configuration of a service using QueryServiceConfigW # or QueryServiceConfig2W # # @param [String] service_name name of the service to query # @return [QUERY_SERVICE_CONFIGW.struct] the configuration of the service def service_start_type(service_name) start_type = nil open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service| query_config(service) do |config| start_type = SERVICE_START_TYPES[config[:dwStartType]] end end # if the service has type AUTO_START, check if it's a delayed service if start_type == :SERVICE_AUTO_START open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service| query_config2(service, SERVICE_CONFIG_DELAYED_AUTO_START_INFO) do |config| return :SERVICE_DELAYED_AUTO_START if config[:fDelayedAutostart] == 1 end end end if start_type.nil? raise Puppet::Error, _("Unknown start type '%{start_type}' for '%{service_name}'") % { start_type: start_type.to_s, service_name: service_name } end start_type end module_function :service_start_type # Query the configuration of a service using QueryServiceConfigW # to find its current logon account # # @return [String] logon_account account currently set for the service's logon # in the format "DOMAIN\Account" or ".\Account" if it's a local account def logon_account(service_name) open_service(service_name, SC_MANAGER_CONNECT, SERVICE_QUERY_CONFIG) do |service| query_config(service) do |config| return config[:lpServiceStartName].read_arbitrary_wide_string_up_to(Puppet::Util::Windows::ADSI::User::MAX_USERNAME_LENGTH) end end end module_function :logon_account # Set the startup configuration of a windows service # # @param [String] service_name the name of the service to modify # @param [Hash] options the configuration to be applied. Expected option keys: # - [Integer] startup_type a code corresponding to a start type for # windows service, see the "Service start type codes" section in the # Puppet::Util::Windows::Service file for the list of available codes # - [String] logon_account the account to be used by the service for logon # - [String] logon_password the provided logon_account's password to be used by the service for logon # - [Bool] delayed whether the service should be started with a delay def set_startup_configuration(service_name, options: {}) options[:startup_type] = SERVICE_START_TYPES.key(options[:startup_type]) || SERVICE_NO_CHANGE options[:logon_account] = wide_string(options[:logon_account]) || FFI::Pointer::NULL options[:logon_password] = wide_string(options[:logon_password]) || FFI::Pointer::NULL open_service(service_name, SC_MANAGER_CONNECT, SERVICE_CHANGE_CONFIG) do |service| success = ChangeServiceConfigW( service, SERVICE_NO_CHANGE, # dwServiceType options[:startup_type], # dwStartType SERVICE_NO_CHANGE, # dwErrorControl FFI::Pointer::NULL, # lpBinaryPathName FFI::Pointer::NULL, # lpLoadOrderGroup FFI::Pointer::NULL, # lpdwTagId FFI::Pointer::NULL, # lpDependencies options[:logon_account], # lpServiceStartName options[:logon_password], # lpPassword FFI::Pointer::NULL # lpDisplayName ) if success == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to update service configuration") end end if options[:startup_type] options[:delayed] ||= false set_startup_mode_delayed(service_name, options[:delayed]) end end module_function :set_startup_configuration # enumerate over all services in all states and return them as a hash # # @return [Hash] a hash containing services: # { 'service name' => { # 'display_name' => 'display name', # 'service_status_process' => SERVICE_STATUS_PROCESS struct # } # } def services services = {} open_scm(SC_MANAGER_ENUMERATE_SERVICE) do |scm| size_required = 0 services_returned = 0 FFI::MemoryPointer.new(:dword) do |bytes_pointer| FFI::MemoryPointer.new(:dword) do |svcs_ret_ptr| FFI::MemoryPointer.new(:dword) do |resume_ptr| resume_ptr.write_dword(0) # Fetch the bytes of memory required to be allocated # for QueryServiceConfigW to return succesfully. This # is done by sending NULL and 0 for the pointer and size # respectively, letting the command fail, then reading the # value of pcbBytesNeeded # # return value will be false from this call, since it's designed # to fail. Just ignore it EnumServicesStatusExW( scm, :SC_ENUM_PROCESS_INFO, ALL_SERVICE_TYPES, SERVICE_STATE_ALL, FFI::Pointer::NULL, 0, bytes_pointer, svcs_ret_ptr, resume_ptr, FFI::Pointer::NULL ) size_required = bytes_pointer.read_dword FFI::MemoryPointer.new(size_required) do |buffer_ptr| resume_ptr.write_dword(0) svcs_ret_ptr.write_dword(0) success = EnumServicesStatusExW( scm, :SC_ENUM_PROCESS_INFO, ALL_SERVICE_TYPES, SERVICE_STATE_ALL, buffer_ptr, buffer_ptr.size, bytes_pointer, svcs_ret_ptr, resume_ptr, FFI::Pointer::NULL ) if success == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to fetch services") end # Now that the buffer is populated with services # we pull the data from memory using pointer arithmetic: # the number of services returned by the function is # available to be read from svcs_ret_ptr, and we iterate # that many times moving the cursor pointer the length of # ENUM_SERVICE_STATUS_PROCESSW.size. This should iterate # over the buffer and extract each struct. services_returned = svcs_ret_ptr.read_dword cursor_ptr = FFI::Pointer.new(ENUM_SERVICE_STATUS_PROCESSW, buffer_ptr) 0.upto(services_returned - 1) do |index| service = ENUM_SERVICE_STATUS_PROCESSW.new(cursor_ptr[index]) services[service[:lpServiceName].read_arbitrary_wide_string_up_to(SERVICENAME_MAX)] = { :display_name => service[:lpDisplayName].read_arbitrary_wide_string_up_to(SERVICENAME_MAX), :service_status_process => service[:ServiceStatusProcess] } end end # buffer_ptr end # resume_ptr end # scvs_ret_ptr end # bytes_ptr end # open_scm services end module_function :services class << self # @api private # Opens a connection to the SCManager on windows then uses that # handle to create a handle to a specific service in windows # corresponding to service_name # # this function takes a block that executes within the context of # the open service handler, and will close the service and SCManager # handles once the block finishes # # @param [string] service_name the name of the service to open # @param [Integer] scm_access code corresponding to the access type requested for the scm # @param [Integer] service_access code corresponding to the access type requested for the service # @yieldparam [:handle] service the windows native handle used to access # the service # @return the result of the block def open_service(service_name, scm_access, service_access, &block) service = FFI::Pointer::NULL_HANDLE result = nil open_scm(scm_access) do |scm| service = OpenServiceW(scm, wide_string(service_name), service_access) raise Puppet::Util::Windows::Error, _("Failed to open a handle to the service") if service == FFI::Pointer::NULL_HANDLE result = yield service end result ensure CloseServiceHandle(service) end private :open_service # @api private # # Opens a handle to the service control manager # # @param [Integer] scm_access code corresponding to the access type requested for the scm def open_scm(scm_access, &block) scm = OpenSCManagerW(FFI::Pointer::NULL, FFI::Pointer::NULL, scm_access) raise Puppet::Util::Windows::Error, _("Failed to open a handle to the service control manager") if scm == FFI::Pointer::NULL_HANDLE yield scm ensure CloseServiceHandle(scm) end private :open_scm # @api private # Transition the service to the specified state. The block should perform # the actual transition. # # @param [String] service_name the name of the service to transition # @param [[Integer]] valid_initial_states an array of valid states that the service can transition from # @param [Integer] final_state the state that the service will transition to # @param [Integer] timeout the minumum number of seconds to wait before timing out def transition_service_state(service_name, valid_initial_states, final_state, timeout, &block) service_access = SERVICE_START | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS open_service(service_name, SC_MANAGER_CONNECT, service_access) do |service| query_status(service) do |status| initial_state = status[:dwCurrentState] # If the service is already in the final_state, then # no further work needs to be done if initial_state == final_state Puppet.debug _("The service is already in the %{final_state} state. No further work needs to be done.") % { final_state: SERVICE_STATES[final_state] } next end # Check that initial_state corresponds to a valid # initial state unless valid_initial_states.include?(initial_state) valid_initial_states_str = valid_initial_states.map do |state| SERVICE_STATES[state] end.join(", ") raise Puppet::Error, _("The service must be in one of the %{valid_initial_states} states to perform this transition. It is currently in the %{current_state} state.") % { valid_initial_states: valid_initial_states_str, current_state: SERVICE_STATES[initial_state] } end # Check if there's a pending transition to the final_state. If so, then wait for # that transition to finish. possible_pending_states = FINAL_STATES.keys.select do |pending_state| # SERVICE_RUNNING has two pending states, SERVICE_START_PENDING and # SERVICE_CONTINUE_PENDING. That is why we need the #select here FINAL_STATES[pending_state] == final_state end if possible_pending_states.include?(initial_state) Puppet.debug _("There is already a pending transition to the %{final_state} state for the %{service_name} service.") % { final_state: SERVICE_STATES[final_state], service_name: service_name } wait_on_pending_state(service, initial_state, timeout) next end # If we are in an unsafe pending state like SERVICE_START_PENDING # or SERVICE_STOP_PENDING, then we want to wait for that pending # transition to finish before transitioning the service state. # The reason we do this is because SERVICE_START_PENDING is when # the service thread is being created and initialized, while # SERVICE_STOP_PENDING is when the service thread is being cleaned # up and destroyed. Thus there is a chance that when the service is # in either of these states, its service thread may not yet be ready # to perform the state transition (it may not even exist). if UNSAFE_PENDING_STATES.include?(initial_state) Puppet.debug _("The service is in the %{pending_state} state, which is an unsafe pending state.") % { pending_state: SERVICE_STATES[initial_state] } wait_on_pending_state(service, initial_state, timeout) initial_state = FINAL_STATES[initial_state] end Puppet.debug _("Transitioning the %{service_name} service from %{initial_state} to %{final_state}") % { service_name: service_name, initial_state: SERVICE_STATES[initial_state], final_state: SERVICE_STATES[final_state] } yield service Puppet.debug _("Waiting for the transition to finish") wait_on_state_transition(service, initial_state, final_state, timeout) end end rescue => detail raise Puppet::Error, _("Failed to transition the %{service_name} service to the %{final_state} state. Detail: %{detail}") % { service_name: service_name, final_state: SERVICE_STATES[final_state], detail: detail }, detail.backtrace end private :transition_service_state # @api private # perform QueryServiceStatusEx on a windows service and return the # result # # @param [:handle] service handle of the service to query # @return [SERVICE_STATUS_PROCESS struct] the result of the query def query_status(service) size_required = nil status = nil # Fetch the bytes of memory required to be allocated # for QueryServiceConfigW to return succesfully. This # is done by sending NULL and 0 for the pointer and size # respectively, letting the command fail, then reading the # value of pcbBytesNeeded FFI::MemoryPointer.new(:lpword) do |bytes_pointer| # return value will be false from this call, since it's designed # to fail. Just ignore it QueryServiceStatusEx( service, :SC_STATUS_PROCESS_INFO, FFI::Pointer::NULL, 0, bytes_pointer ) size_required = bytes_pointer.read_dword FFI::MemoryPointer.new(size_required) do |ssp_ptr| status = SERVICE_STATUS_PROCESS.new(ssp_ptr) success = QueryServiceStatusEx( service, :SC_STATUS_PROCESS_INFO, ssp_ptr, size_required, bytes_pointer ) if success == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Service query failed") end yield status end end end private :query_status # @api private # perform QueryServiceConfigW on a windows service and return the # result # # @param [:handle] service handle of the service to query # @return [QUERY_SERVICE_CONFIGW struct] the result of the query def query_config(service, &block) config = nil size_required = nil # Fetch the bytes of memory required to be allocated # for QueryServiceConfigW to return succesfully. This # is done by sending NULL and 0 for the pointer and size # respectively, letting the command fail, then reading the # value of pcbBytesNeeded FFI::MemoryPointer.new(:lpword) do |bytes_pointer| # return value will be false from this call, since it's designed # to fail. Just ignore it QueryServiceConfigW(service, FFI::Pointer::NULL, 0, bytes_pointer) size_required = bytes_pointer.read_dword FFI::MemoryPointer.new(size_required) do |ssp_ptr| config = QUERY_SERVICE_CONFIGW.new(ssp_ptr) success = QueryServiceConfigW( service, ssp_ptr, size_required, bytes_pointer ) if success == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Service query failed") end yield config end end end private :query_config # @api private # perform QueryServiceConfig2W on a windows service and return the # result # # @param [:handle] service handle of the service to query # @param [Integer] info_level the configuration information to be queried # @return [QUERY_SERVICE_CONFIG2W struct] the result of the query def query_config2(service, info_level, &block) config = nil size_required = nil # Fetch the bytes of memory required to be allocated # for QueryServiceConfig2W to return succesfully. This # is done by sending NULL and 0 for the pointer and size # respectively, letting the command fail, then reading the # value of pcbBytesNeeded FFI::MemoryPointer.new(:lpword) do |bytes_pointer| # return value will be false from this call, since it's designed # to fail. Just ignore it QueryServiceConfig2W(service, info_level, FFI::Pointer::NULL, 0, bytes_pointer) size_required = bytes_pointer.read_dword FFI::MemoryPointer.new(size_required) do |ssp_ptr| # We need to supply the appropriate struct to be created based on # the info_level case info_level when SERVICE_CONFIG_DELAYED_AUTO_START_INFO config = SERVICE_DELAYED_AUTO_START_INFO.new(ssp_ptr) end success = QueryServiceConfig2W( service, info_level, ssp_ptr, size_required, bytes_pointer ) if success == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Service query for %{parameter_name} failed") % { parameter_name: SERVICE_CONFIG_TYPES[info_level] } end yield config end end end private :query_config2 # @api private # Sets an optional parameter on a service by calling # ChangeServiceConfig2W # # @param [String] service_name name of service # @param [Integer] change parameter to change # @param [struct] value appropriate struct based on the parameter to change def set_optional_parameter(service_name, change, value) open_service(service_name, SC_MANAGER_CONNECT, SERVICE_CHANGE_CONFIG) do |service| success = ChangeServiceConfig2W( service, change, # dwInfoLevel value # lpInfo ) if success == FFI::WIN32_FALSE raise Puppet::Util.windows::Error, _("Failed to update service %{change} configuration") % { change: change } end end end private :set_optional_parameter # @api private # Controls the delayed auto-start setting of a service # # @param [String] service_name name of service # @param [Bool] delayed whether the service should be started with a delay or not def set_startup_mode_delayed(service_name, delayed) delayed_start = SERVICE_DELAYED_AUTO_START_INFO.new delayed_start[:fDelayedAutostart] = delayed set_optional_parameter(service_name, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayed_start) end private :set_startup_mode_delayed # @api private # Sends a service control signal to a service # # @param [:handle] service handle to the service # @param [Integer] signal the service control signal to send def send_service_control_signal(service, signal) FFI::MemoryPointer.new(SERVICE_STATUS.size) do |status_ptr| status = SERVICE_STATUS.new(status_ptr) if ControlService(service, signal, status) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}. Reason for failure:") % { control_signal: SERVICE_CONTROL_SIGNALS[signal], current_state: SERVICE_STATES[status[:dwCurrentState]] } end end end # @api private # Waits for a service to transition from one state to # another state. # # @param [:handle] service handle to the service to wait on # @param [Integer] initial_state the state that the service is transitioning from. # @param [Integer] final_state the state that the service is transitioning to # @param [Integer] timeout the minumum number of seconds to wait before timing out def wait_on_state_transition(service, initial_state, final_state, timeout) # Get the pending state for this transition. Note that SERVICE_RUNNING # has two possible pending states, which is why we need this logic. if final_state != SERVICE_RUNNING pending_state = FINAL_STATES.key(final_state) elsif initial_state == SERVICE_STOPPED # SERVICE_STOPPED => SERVICE_RUNNING pending_state = SERVICE_START_PENDING else # SERVICE_PAUSED => SERVICE_RUNNING pending_state = SERVICE_CONTINUE_PENDING end # Wait for the transition to finish state = nil elapsed_time = 0 while elapsed_time <= timeout query_status(service) do |status| state = status[:dwCurrentState] return if state == final_state if state == pending_state Puppet.debug _("The service transitioned to the %{pending_state} state.") % { pending_state: SERVICE_STATES[pending_state] } wait_on_pending_state(service, pending_state, timeout) return end sleep(1) elapsed_time += 1 end end # Timed out while waiting for the transition to finish. Raise an error # We can still use the state variable read from the FFI struct because # FFI creates new Integer objects during an assignment of an integer value # stored in an FFI struct. We verified that the '=' operater is safe # from the freed memory since the new ruby object created during the # assignment will remain in ruby memory and remain immutable and constant. raise Puppet::Error, _("Timed out while waiting for the service to transition from %{initial_state} to %{final_state} OR from %{initial_state} to %{pending_state} to %{final_state}. The service's current state is %{current_state}.") % { initial_state: SERVICE_STATES[initial_state], final_state: SERVICE_STATES[final_state], pending_state: SERVICE_STATES[pending_state], current_state: SERVICE_STATES[state] } end private :wait_on_state_transition # @api private # Waits for a service to finish transitioning from # a pending state. The service must be in the pending state # before invoking this routine. # # @param [:handle] service handle to the service to wait on # @param [Integer] pending_state the pending state # @param [Integer] timeout the minumum number of seconds to wait before timing out def wait_on_pending_state(service, pending_state, timeout) final_state = FINAL_STATES[pending_state] Puppet.debug _("Waiting for the pending transition to the %{final_state} state to finish.") % { final_state: SERVICE_STATES[final_state] } elapsed_time = 0 last_checkpoint = -1 loop do query_status(service) do |status| state = status[:dwCurrentState] checkpoint = status[:dwCheckPoint] wait_hint = status[:dwWaitHint] # Check if our service has finished transitioning to # the final_state OR if an unexpected transition # has occurred return if state == final_state unless state == pending_state raise Puppet::Error, _("Unexpected transition to the %{current_state} state while waiting for the pending transition from %{pending_state} to %{final_state} to finish.") % { current_state: SERVICE_STATES[state], pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state] } end # Check if any progress has been made since our last sleep # using the dwCheckPoint. If no progress has been made then # check if we've timed out, and raise an error if so if checkpoint > last_checkpoint elapsed_time = 0 last_checkpoint = checkpoint else wait_hint = milliseconds_to_seconds(status[:dwWaitHint]) timeout = wait_hint < timeout ? timeout : wait_hint if elapsed_time >= timeout raise Puppet::Error, _("Timed out while waiting for the pending transition from %{pending_state} to %{final_state} to finish. The current state is %{current_state}.") % { pending_state: SERVICE_STATES[pending_state], final_state: SERVICE_STATES[final_state], current_state: SERVICE_STATES[state] } end end wait_time = wait_hint_to_wait_time(wait_hint) # Wait a bit before rechecking the service's state sleep(wait_time) elapsed_time += wait_time end end end private :wait_on_pending_state # @api private # # create a usable wait time to wait between querying the service. # # @param [Integer] wait_hint the wait hint of a service in milliseconds # @return [Integer] the time to wait in seconds between querying the service def wait_hint_to_wait_time(wait_hint) # Wait 1/10th the wait_hint, but no less than 1 and # no more than 10 seconds wait_time = milliseconds_to_seconds(wait_hint) / 10; wait_time = 1 if wait_time < 1 wait_time = 10 if wait_time > 10 wait_time end private :wait_hint_to_wait_time # @api private # # process the wait hint listed by a service to something # usable by ruby sleep # # @param [Integer] wait_hint the wait hint of a service in milliseconds # @return [Integer] wait_hint in seconds def milliseconds_to_seconds(wait_hint) wait_hint / 1000; end private :milliseconds_to_seconds end end end puppetlabs-puppet-789f600/lib/puppet/util/windows/sid.rb000066400000000000000000000260751470131746300233470ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/util/windows' module Puppet::Util::Windows module SID require 'ffi' extend FFI::Library # missing from Windows::Error ERROR_NONE_MAPPED = 1332 ERROR_INVALID_SID_STRUCTURE = 1337 ERROR_TRUSTED_DOMAIN_FAILURE = 1788 ERROR_TRUSTED_RELATIONSHIP_FAILURE = 1789 # Well Known SIDs Null = 'S-1-0' Nobody = 'S-1-0-0' World = 'S-1-1' Everyone = 'S-1-1-0' Local = 'S-1-2' Creator = 'S-1-3' CreatorOwner = 'S-1-3-0' CreatorGroup = 'S-1-3-1' CreatorOwnerServer = 'S-1-3-2' CreatorGroupServer = 'S-1-3-3' NonUnique = 'S-1-4' Nt = 'S-1-5' Dialup = 'S-1-5-1' Network = 'S-1-5-2' Batch = 'S-1-5-3' Interactive = 'S-1-5-4' Service = 'S-1-5-6' Anonymous = 'S-1-5-7' Proxy = 'S-1-5-8' EnterpriseDomainControllers = 'S-1-5-9' PrincipalSelf = 'S-1-5-10' AuthenticatedUsers = 'S-1-5-11' RestrictedCode = 'S-1-5-12' TerminalServerUsers = 'S-1-5-13' LocalSystem = 'S-1-5-18' NtLocal = 'S-1-5-19' NtNetwork = 'S-1-5-20' BuiltinAdministrators = 'S-1-5-32-544' BuiltinUsers = 'S-1-5-32-545' Guests = 'S-1-5-32-546' PowerUsers = 'S-1-5-32-547' AccountOperators = 'S-1-5-32-548' ServerOperators = 'S-1-5-32-549' PrintOperators = 'S-1-5-32-550' BackupOperators = 'S-1-5-32-551' Replicators = 'S-1-5-32-552' AllAppPackages = 'S-1-15-2-1' # Convert an account name, e.g. 'Administrators' into a SID string, # 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. Returns nil if the account doesn't exist. def name_to_sid(name) sid = name_to_principal(name) sid ? sid.sid : nil end module_function :name_to_sid # Convert an account name, e.g. 'Administrators' into a Principal::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. # This method returns a SID::Principal with the account, domain, SID, etc def name_to_principal(name, allow_unresolved = false) # Apparently, we accept a symbol.. name = name.to_s.strip if name # if name is a SID string, convert it to raw bytes for use with lookup_account_sid raw_sid_bytes = nil begin string_to_sid_ptr(name) do |sid_ptr| raw_sid_bytes = sid_ptr.read_array_of_uchar(get_length_sid(sid_ptr)) end rescue => e # Avoid debug logs pollution with valid account names # https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsidtosidw#return-value Puppet.debug("Could not retrieve raw SID bytes from '#{name}': #{e.message}") unless e.code == ERROR_INVALID_SID_STRUCTURE end raw_sid_bytes ? Principal.lookup_account_sid(raw_sid_bytes) : Principal.lookup_account_name(name) rescue => e Puppet.debug(e.message.to_s) (allow_unresolved && raw_sid_bytes) ? unresolved_principal(name, raw_sid_bytes) : nil end module_function :name_to_principal class << self; alias name_to_sid_object name_to_principal; end # Converts an octet string array of bytes to a SID::Principal 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. # This method returns a SID::Principal with the account, domain, SID, etc def octet_string_to_principal(bytes) if !bytes || !bytes.respond_to?('pack') || bytes.empty? raise Puppet::Util::Windows::Error, _("Octet string must be an array of bytes") end Principal.lookup_account_sid(bytes) end module_function :octet_string_to_principal class << self; alias octet_string_to_sid_object octet_string_to_principal; end # Converts a COM instance of IAdsUser or IAdsGroup to a SID::Principal object, # Raises an Error for nil or an object without an objectSID / Name property. # This method returns a SID::Principal with the account, domain, SID, etc # This method will return instances even when the SID is unresolvable, as # may be the case when domain users have been added to local groups, but # removed from the domain def ads_to_principal(ads_object) if !ads_object || !ads_object.respond_to?(:ole_respond_to?) || !ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name) raise Puppet::Error, "ads_object must be an IAdsUser or IAdsGroup instance" end octet_string_to_principal(ads_object.objectSID) rescue Puppet::Util::Windows::Error => e # if the error is not a lookup / mapping problem, immediately re-raise raise if e.code != ERROR_NONE_MAPPED # if the Name property isn't formatted like a SID, OR if !valid_sid?(ads_object.Name) || # if the objectSID doesn't match the Name property, also raise ((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name) raise Puppet::Error.new("ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})", e) end unresolved_principal(ads_object.Name, ads_object.objectSID) end module_function :ads_to_principal # 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. def sid_to_name(value) sid_bytes = [] begin string_to_sid_ptr(value) do |ptr| sid_bytes = ptr.read_array_of_uchar(get_length_sid(ptr)) end rescue Puppet::Util::Windows::Error => e raise if e.code != ERROR_INVALID_SID_STRUCTURE end Principal.lookup_account_sid(sid_bytes).domain_account rescue nil end module_function :sid_to_name # https://stackoverflow.com/a/1792930 - 68 bytes, 184 characters in a string MAXIMUM_SID_STRING_LENGTH = 184 # Convert a SID pointer to a SID string, e.g. "S-1-5-32-544". def sid_ptr_to_string(psid) if !psid.is_a?(FFI::Pointer) || IsValidSid(psid) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Invalid SID") end sid_string = nil FFI::MemoryPointer.new(:pointer, 1) do |buffer_ptr| if ConvertSidToStringSidW(psid, buffer_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to convert binary SID") end buffer_ptr.read_win32_local_pointer do |wide_string_ptr| if wide_string_ptr.null? raise Puppet::Error, _("ConvertSidToStringSidW failed to allocate buffer for sid") end sid_string = wide_string_ptr.read_arbitrary_wide_string_up_to(MAXIMUM_SID_STRING_LENGTH) end end sid_string end module_function :sid_ptr_to_string # Convert a SID string, e.g. "S-1-5-32-544" to a pointer (containing the # address of the binary SID structure). The returned value can be used in # Win32 APIs that expect a PSID, e.g. IsValidSid. The account for this # SID may or may not exist. def string_to_sid_ptr(string_sid, &block) FFI::MemoryPointer.from_string_to_wide_string(string_sid) do |lpcwstr| FFI::MemoryPointer.new(:pointer, 1) do |sid_ptr_ptr| if ConvertStringSidToSidW(lpcwstr, sid_ptr_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to convert string SID: %{string_sid}") % { string_sid: string_sid } end sid_ptr_ptr.read_win32_local_pointer do |sid_ptr| yield sid_ptr end end end # yielded sid_ptr has already had LocalFree called, nothing to return nil end module_function :string_to_sid_ptr # Return true if the string is a valid SID, e.g. "S-1-5-32-544", false otherwise. def valid_sid?(string_sid) valid = false begin string_to_sid_ptr(string_sid) { |ptr| valid = !ptr.nil? && !ptr.null? } rescue Puppet::Util::Windows::Error => e raise if e.code != ERROR_INVALID_SID_STRUCTURE end valid end module_function :valid_sid? def get_length_sid(sid_ptr) # MSDN states IsValidSid should be called on pointer first if !sid_ptr.is_a?(FFI::Pointer) || IsValidSid(sid_ptr) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Invalid SID") end GetLengthSid(sid_ptr) end module_function :get_length_sid def octet_string_to_sid_string(sid_bytes) sid_string = nil FFI::MemoryPointer.new(:byte, sid_bytes.length) do |sid_ptr| sid_ptr.write_array_of_uchar(sid_bytes) sid_string = Puppet::Util::Windows::SID.sid_ptr_to_string(sid_ptr) end sid_string end module_function :octet_string_to_sid_string # @api private def self.unresolved_principal(name, sid_bytes) Principal.new( name, # account sid_bytes, # sid_bytes name, # sid string nil, # domain # https://msdn.microsoft.com/en-us/library/cc245534.aspx?f=255&MSPPError=-2147217396 # Indicates that the type of object could not be determined. For example, no object with that SID exists. :SidTypeUnknown ) end ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379151(v=vs.85).aspx # BOOL WINAPI IsValidSid( # _In_ PSID pSid # ); ffi_lib :advapi32 attach_function_private :IsValidSid, [:pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376399(v=vs.85).aspx # BOOL ConvertSidToStringSid( # _In_ PSID Sid, # _Out_ LPTSTR *StringSid # ); ffi_lib :advapi32 attach_function_private :ConvertSidToStringSidW, [:pointer, :pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376402(v=vs.85).aspx # BOOL WINAPI ConvertStringSidToSid( # _In_ LPCTSTR StringSid, # _Out_ PSID *Sid # ); ffi_lib :advapi32 attach_function_private :ConvertStringSidToSidW, [:lpcwstr, :pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446642(v=vs.85).aspx # DWORD WINAPI GetLengthSid( # _In_ PSID pSid # ); ffi_lib :advapi32 attach_function_private :GetLengthSid, [:pointer], :dword end end puppetlabs-puppet-789f600/lib/puppet/util/windows/string.rb000066400000000000000000000005451470131746300240700ustar00rootroot00000000000000# frozen_string_literal: true module Puppet module Util module Windows module String def wide_string(str) # if given a nil string, assume caller wants to pass a nil pointer to win32 return nil if str.nil? str.encode('UTF-16LE') end module_function :wide_string end end end end puppetlabs-puppet-789f600/lib/puppet/util/windows/user.rb000066400000000000000000000556371470131746300235540ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../../puppet/util/windows' require 'ffi' module Puppet::Util::Windows::User extend Puppet::Util::Windows::String extend FFI::Library def admin? return false unless check_token_membership # if Vista or later, check for unrestricted process token elevated_supported = Puppet::Util::Windows::Process.supports_elevated_security? elevated_supported ? Puppet::Util::Windows::Process.elevated_security? : true end module_function :admin? # The name of the account in all locales is `LocalSystem`. `.\LocalSystem` or `ComputerName\LocalSystem' can also be used. # This account is not recognized by the security subsystem, so you cannot specify its name in a call to the `LookupAccountName` function. # https://docs.microsoft.com/en-us/windows/win32/services/localsystem-account def localsystem?(name) ["LocalSystem", ".\\LocalSystem", "#{Puppet::Util::Windows::ADSI.computer_name}\\LocalSystem"].any? { |s| s.casecmp(name) == 0 } end module_function :localsystem? # Check if a given user is one of the default system accounts # These accounts do not have a password and all checks done through logon attempt will fail # https://docs.microsoft.com/en-us/windows/security/identity-protection/access-control/local-accounts#default-local-system-accounts def default_system_account?(name) user_sid = Puppet::Util::Windows::SID.name_to_sid(name) [Puppet::Util::Windows::SID::LocalSystem, Puppet::Util::Windows::SID::NtLocal, Puppet::Util::Windows::SID::NtNetwork].include?(user_sid) end module_function :default_system_account? # https://msdn.microsoft.com/en-us/library/windows/desktop/ee207397(v=vs.85).aspx SECURITY_MAX_SID_SIZE = 68 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx # These error codes indicate successful authentication but failure to # logon for a separate reason ERROR_ACCOUNT_RESTRICTION = 1327 ERROR_INVALID_LOGON_HOURS = 1328 ERROR_INVALID_WORKSTATION = 1329 ERROR_ACCOUNT_DISABLED = 1331 def check_token_membership is_admin = false FFI::MemoryPointer.new(:byte, SECURITY_MAX_SID_SIZE) do |sid_pointer| FFI::MemoryPointer.new(:dword, 1) do |size_pointer| size_pointer.write_uint32(SECURITY_MAX_SID_SIZE) if CreateWellKnownSid(:WinBuiltinAdministratorsSid, FFI::Pointer::NULL, sid_pointer, size_pointer) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to create administrators SID") end end if IsValidSid(sid_pointer) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Invalid SID") end FFI::MemoryPointer.new(:win32_bool, 1) do |ismember_pointer| if CheckTokenMembership(FFI::Pointer::NULL_HANDLE, sid_pointer, ismember_pointer) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to check membership") end # Is administrators SID enabled in calling thread's access token? is_admin = ismember_pointer.read_win32_bool end end is_admin end module_function :check_token_membership def password_is?(name, password, domain = '.') logon_user(name, password, domain) { |token| } rescue Puppet::Util::Windows::Error => detail authenticated_error_codes = Set[ ERROR_ACCOUNT_RESTRICTION, ERROR_INVALID_LOGON_HOURS, ERROR_INVALID_WORKSTATION, ERROR_ACCOUNT_DISABLED, ] authenticated_error_codes.include?(detail.code) end module_function :password_is? def logon_user(name, password, domain = '.', &block) fLOGON32_PROVIDER_DEFAULT = 0 fLOGON32_LOGON_INTERACTIVE = 2 fLOGON32_LOGON_NETWORK = 3 token = nil begin FFI::MemoryPointer.new(:handle, 1) do |token_pointer| # try logon using network else try logon using interactive mode if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_INTERACTIVE, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to logon user %{name}") % { name: name.inspect } end end yield token = token_pointer.read_handle end ensure FFI::WIN32.CloseHandle(token) if token end # token has been closed by this point true end module_function :logon_user def self.logon_user_by_logon_type(name, domain, password, logon_type, logon_provider, token) LogonUserW(wide_string(name), wide_string(domain), password.nil? ? FFI::Pointer::NULL : wide_string(password), logon_type, logon_provider, token) end private_class_method :logon_user_by_logon_type def load_profile(user, password) logon_user(user, password) do |token| FFI::MemoryPointer.from_string_to_wide_string(user) do |lpUserName| pi = PROFILEINFO.new pi[:dwSize] = PROFILEINFO.size pi[:dwFlags] = 1 # PI_NOUI - prevents display of profile error msgs pi[:lpUserName] = lpUserName # Load the profile. Since it doesn't exist, it will be created if LoadUserProfileW(token, pi.pointer) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to load user profile %{user}") % { user: user.inspect } end Puppet.debug("Loaded profile for #{user}") if UnloadUserProfile(token, pi[:hProfile]) == FFI::WIN32_FALSE raise Puppet::Util::Windows::Error, _("Failed to unload user profile %{user}") % { user: user.inspect } end end end end module_function :load_profile def get_rights(name) user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) return "" unless user_info rights = [] rights_pointer = FFI::MemoryPointer.new(:pointer) number_of_rights = FFI::MemoryPointer.new(:ulong) sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) new_lsa_policy_handle do |policy_handle| result = LsaEnumerateAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, number_of_rights) check_lsa_nt_status_and_raise_failures(result, "LsaEnumerateAccountRights") end number_of_rights.read_ulong.times do |index| right = LSA_UNICODE_STRING.new(rights_pointer.read_pointer + index * LSA_UNICODE_STRING.size) rights << right[:Buffer].read_arbitrary_wide_string_up_to end result = LsaFreeMemory(rights_pointer.read_pointer) check_lsa_nt_status_and_raise_failures(result, "LsaFreeMemory") rights.join(",") end module_function :get_rights def set_rights(name, rights) rights_pointer = new_lsa_unicode_strings_pointer(rights) user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) new_lsa_policy_handle do |policy_handle| result = LsaAddAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, rights.size) check_lsa_nt_status_and_raise_failures(result, "LsaAddAccountRights") end end module_function :set_rights def remove_rights(name, rights) rights_pointer = new_lsa_unicode_strings_pointer(rights) user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) new_lsa_policy_handle do |policy_handle| result = LsaRemoveAccountRights(policy_handle.read_pointer, sid_pointer, false, rights_pointer, rights.size) check_lsa_nt_status_and_raise_failures(result, "LsaRemoveAccountRights") end end module_function :remove_rights # ACCESS_MASK flags for Policy Objects # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/b61b7268-987a-420b-84f9-6c75f8dc8558 POLICY_VIEW_LOCAL_INFORMATION = 0x00000001 POLICY_VIEW_AUDIT_INFORMATION = 0x00000002 POLICY_GET_PRIVATE_INFORMATION = 0x00000004 POLICY_TRUST_ADMIN = 0x00000008 POLICY_CREATE_ACCOUNT = 0x00000010 POLICY_CREATE_SECRET = 0x00000020 POLICY_CREATE_PRIVILEGE = 0x00000040 POLICY_SET_DEFAULT_QUOTA_LIMITS = 0x00000080 POLICY_SET_AUDIT_REQUIREMENTS = 0x00000100 POLICY_AUDIT_LOG_ADMIN = 0x00000200 POLICY_SERVER_ADMIN = 0x00000400 POLICY_LOOKUP_NAMES = 0x00000800 POLICY_NOTIFICATION = 0x00001000 def self.new_lsa_policy_handle access = 0 access |= POLICY_LOOKUP_NAMES access |= POLICY_CREATE_ACCOUNT policy_handle = FFI::MemoryPointer.new(:pointer) result = LsaOpenPolicy(nil, LSA_OBJECT_ATTRIBUTES.new, access, policy_handle) check_lsa_nt_status_and_raise_failures(result, "LsaOpenPolicy") begin yield policy_handle ensure result = LsaClose(policy_handle.read_pointer) check_lsa_nt_status_and_raise_failures(result, "LsaClose") end end private_class_method :new_lsa_policy_handle def self.new_lsa_unicode_strings_pointer(strings) lsa_unicode_strings_pointer = FFI::MemoryPointer.new(LSA_UNICODE_STRING, strings.size) strings.each_with_index do |string, index| lsa_string = LSA_UNICODE_STRING.new(lsa_unicode_strings_pointer + index * LSA_UNICODE_STRING.size) lsa_string[:Buffer] = FFI::MemoryPointer.from_string(wide_string(string)) lsa_string[:Length] = string.length * 2 lsa_string[:MaximumLength] = lsa_string[:Length] + 2 end lsa_unicode_strings_pointer end private_class_method :new_lsa_unicode_strings_pointer # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d def self.check_lsa_nt_status_and_raise_failures(status, method_name) error_code = LsaNtStatusToWinError(status) error_reason = case error_code.to_s(16) when '0' # ERROR_SUCCESS return # Method call succeded when '2' # ERROR_FILE_NOT_FOUND return # No rights/privilleges assigned to given user when '5' # ERROR_ACCESS_DENIED "Access is denied. Please make sure that puppet is running as administrator." when '521' # ERROR_NO_SUCH_PRIVILEGE "One or more of the given rights/privilleges are incorrect." when '6ba' # RPC_S_SERVER_UNAVAILABLE "The RPC server is unavailable or given domain name is invalid." end raise Puppet::Error, "Calling `#{method_name}` returned 'Win32 Error Code 0x%08X'. #{error_reason}" % error_code end private_class_method :check_lsa_nt_status_and_raise_failures ffi_convention :stdcall # https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx # BOOL LogonUser( # _In_ LPTSTR lpszUsername, # _In_opt_ LPTSTR lpszDomain, # _In_opt_ LPTSTR lpszPassword, # _In_ DWORD dwLogonType, # _In_ DWORD dwLogonProvider, # _Out_ PHANDLE phToken # ); ffi_lib :advapi32 attach_function_private :LogonUserW, [:lpwstr, :lpwstr, :lpwstr, :dword, :dword, :phandle], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/bb773378(v=vs.85).aspx # typedef struct _PROFILEINFO { # DWORD dwSize; # DWORD dwFlags; # LPTSTR lpUserName; # LPTSTR lpProfilePath; # LPTSTR lpDefaultPath; # LPTSTR lpServerName; # LPTSTR lpPolicyPath; # HANDLE hProfile; # } PROFILEINFO, *LPPROFILEINFO; # technically # NOTE: that for structs, buffer_* (lptstr alias) cannot be used class PROFILEINFO < FFI::Struct layout :dwSize, :dword, :dwFlags, :dword, :lpUserName, :pointer, :lpProfilePath, :pointer, :lpDefaultPath, :pointer, :lpServerName, :pointer, :lpPolicyPath, :pointer, :hProfile, :handle end # https://msdn.microsoft.com/en-us/library/windows/desktop/bb762281(v=vs.85).aspx # BOOL WINAPI LoadUserProfile( # _In_ HANDLE hToken, # _Inout_ LPPROFILEINFO lpProfileInfo # ); ffi_lib :userenv attach_function_private :LoadUserProfileW, [:handle, :pointer], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/bb762282(v=vs.85).aspx # BOOL WINAPI UnloadUserProfile( # _In_ HANDLE hToken, # _In_ HANDLE hProfile # ); ffi_lib :userenv attach_function_private :UnloadUserProfile, [:handle, :handle], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa376389(v=vs.85).aspx # BOOL WINAPI CheckTokenMembership( # _In_opt_ HANDLE TokenHandle, # _In_ PSID SidToCheck, # _Out_ PBOOL IsMember # ); ffi_lib :advapi32 attach_function_private :CheckTokenMembership, [:handle, :pointer, :pbool], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379650(v=vs.85).aspx # rubocop:disable Layout/SpaceBeforeComma WELL_KNOWN_SID_TYPE = enum( :WinNullSid , 0, :WinWorldSid , 1, :WinLocalSid , 2, :WinCreatorOwnerSid , 3, :WinCreatorGroupSid , 4, :WinCreatorOwnerServerSid , 5, :WinCreatorGroupServerSid , 6, :WinNtAuthoritySid , 7, :WinDialupSid , 8, :WinNetworkSid , 9, :WinBatchSid , 10, :WinInteractiveSid , 11, :WinServiceSid , 12, :WinAnonymousSid , 13, :WinProxySid , 14, :WinEnterpriseControllersSid , 15, :WinSelfSid , 16, :WinAuthenticatedUserSid , 17, :WinRestrictedCodeSid , 18, :WinTerminalServerSid , 19, :WinRemoteLogonIdSid , 20, :WinLogonIdsSid , 21, :WinLocalSystemSid , 22, :WinLocalServiceSid , 23, :WinNetworkServiceSid , 24, :WinBuiltinDomainSid , 25, :WinBuiltinAdministratorsSid , 26, :WinBuiltinUsersSid , 27, :WinBuiltinGuestsSid , 28, :WinBuiltinPowerUsersSid , 29, :WinBuiltinAccountOperatorsSid , 30, :WinBuiltinSystemOperatorsSid , 31, :WinBuiltinPrintOperatorsSid , 32, :WinBuiltinBackupOperatorsSid , 33, :WinBuiltinReplicatorSid , 34, :WinBuiltinPreWindows2000CompatibleAccessSid , 35, :WinBuiltinRemoteDesktopUsersSid , 36, :WinBuiltinNetworkConfigurationOperatorsSid , 37, :WinAccountAdministratorSid , 38, :WinAccountGuestSid , 39, :WinAccountKrbtgtSid , 40, :WinAccountDomainAdminsSid , 41, :WinAccountDomainUsersSid , 42, :WinAccountDomainGuestsSid , 43, :WinAccountComputersSid , 44, :WinAccountControllersSid , 45, :WinAccountCertAdminsSid , 46, :WinAccountSchemaAdminsSid , 47, :WinAccountEnterpriseAdminsSid , 48, :WinAccountPolicyAdminsSid , 49, :WinAccountRasAndIasServersSid , 50, :WinNTLMAuthenticationSid , 51, :WinDigestAuthenticationSid , 52, :WinSChannelAuthenticationSid , 53, :WinThisOrganizationSid , 54, :WinOtherOrganizationSid , 55, :WinBuiltinIncomingForestTrustBuildersSid , 56, :WinBuiltinPerfMonitoringUsersSid , 57, :WinBuiltinPerfLoggingUsersSid , 58, :WinBuiltinAuthorizationAccessSid , 59, :WinBuiltinTerminalServerLicenseServersSid , 60, :WinBuiltinDCOMUsersSid , 61, :WinBuiltinIUsersSid , 62, :WinIUserSid , 63, :WinBuiltinCryptoOperatorsSid , 64, :WinUntrustedLabelSid , 65, :WinLowLabelSid , 66, :WinMediumLabelSid , 67, :WinHighLabelSid , 68, :WinSystemLabelSid , 69, :WinWriteRestrictedCodeSid , 70, :WinCreatorOwnerRightsSid , 71, :WinCacheablePrincipalsGroupSid , 72, :WinNonCacheablePrincipalsGroupSid , 73, :WinEnterpriseReadonlyControllersSid , 74, :WinAccountReadonlyControllersSid , 75, :WinBuiltinEventLogReadersGroup , 76, :WinNewEnterpriseReadonlyControllersSid , 77, :WinBuiltinCertSvcDComAccessGroup , 78, :WinMediumPlusLabelSid , 79, :WinLocalLogonSid , 80, :WinConsoleLogonSid , 81, :WinThisOrganizationCertificateSid , 82, :WinApplicationPackageAuthoritySid , 83, :WinBuiltinAnyPackageSid , 84, :WinCapabilityInternetClientSid , 85, :WinCapabilityInternetClientServerSid , 86, :WinCapabilityPrivateNetworkClientServerSid , 87, :WinCapabilityPicturesLibrarySid , 88, :WinCapabilityVideosLibrarySid , 89, :WinCapabilityMusicLibrarySid , 90, :WinCapabilityDocumentsLibrarySid , 91, :WinCapabilitySharedUserCertificatesSid , 92, :WinCapabilityEnterpriseAuthenticationSid , 93, :WinCapabilityRemovableStorageSid , 94 ) # rubocop:enable Layout/SpaceBeforeComma # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446585(v=vs.85).aspx # BOOL WINAPI CreateWellKnownSid( # _In_ WELL_KNOWN_SID_TYPE WellKnownSidType, # _In_opt_ PSID DomainSid, # _Out_opt_ PSID pSid, # _Inout_ DWORD *cbSid # ); ffi_lib :advapi32 attach_function_private :CreateWellKnownSid, [WELL_KNOWN_SID_TYPE, :pointer, :pointer, :lpdword], :win32_bool # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379151(v=vs.85).aspx # BOOL WINAPI IsValidSid( # _In_ PSID pSid # ); ffi_lib :advapi32 attach_function_private :IsValidSid, [:pointer], :win32_bool # https://docs.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_object_attributes # typedef struct _LSA_OBJECT_ATTRIBUTES { # ULONG Length; # HANDLE RootDirectory; # PLSA_UNICODE_STRING ObjectName; # ULONG Attributes; # PVOID SecurityDescriptor; # PVOID SecurityQualityOfService; # } LSA_OBJECT_ATTRIBUTES, *PLSA_OBJECT_ATTRIBUTES; class LSA_OBJECT_ATTRIBUTES < FFI::Struct layout :Length, :ulong, :RootDirectory, :handle, :ObjectName, :plsa_unicode_string, :Attributes, :ulong, :SecurityDescriptor, :pvoid, :SecurityQualityOfService, :pvoid end # https://docs.microsoft.com/en-us/windows/win32/api/lsalookup/ns-lsalookup-lsa_unicode_string # typedef struct _LSA_UNICODE_STRING { # USHORT Length; # USHORT MaximumLength; # PWSTR Buffer; # } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING; class LSA_UNICODE_STRING < FFI::Struct layout :Length, :ushort, :MaximumLength, :ushort, :Buffer, :pwstr end # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaenumerateaccountrights # https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/user-rights-assignment # NTSTATUS LsaEnumerateAccountRights( # LSA_HANDLE PolicyHandle, # PSID AccountSid, # PLSA_UNICODE_STRING *UserRights, # PULONG CountOfRights # ); ffi_lib :advapi32 attach_function_private :LsaEnumerateAccountRights, [:lsa_handle, :psid, :plsa_unicode_string, :pulong], :ntstatus # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaaddaccountrights # NTSTATUS LsaAddAccountRights( # LSA_HANDLE PolicyHandle, # PSID AccountSid, # PLSA_UNICODE_STRING UserRights, # ULONG CountOfRights # ); ffi_lib :advapi32 attach_function_private :LsaAddAccountRights, [:lsa_handle, :psid, :plsa_unicode_string, :ulong], :ntstatus # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaremoveaccountrights # NTSTATUS LsaRemoveAccountRights( # LSA_HANDLE PolicyHandle, # PSID AccountSid, # BOOLEAN AllRights, # PLSA_UNICODE_STRING UserRights, # ULONG CountOfRights # ); ffi_lib :advapi32 attach_function_private :LsaRemoveAccountRights, [:lsa_handle, :psid, :bool, :plsa_unicode_string, :ulong], :ntstatus # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaopenpolicy # NTSTATUS LsaOpenPolicy( # PLSA_UNICODE_STRING SystemName, # PLSA_OBJECT_ATTRIBUTES ObjectAttributes, # ACCESS_MASK DesiredAccess, # PLSA_HANDLE PolicyHandle # ); ffi_lib :advapi32 attach_function_private :LsaOpenPolicy, [:plsa_unicode_string, :plsa_object_attributes, :access_mask, :plsa_handle], :ntstatus # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsaclose # NTSTATUS LsaClose( # LSA_HANDLE ObjectHandle # ); ffi_lib :advapi32 attach_function_private :LsaClose, [:lsa_handle], :ntstatus # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsafreememory # NTSTATUS LsaFreeMemory( # PVOID Buffer # ); ffi_lib :advapi32 attach_function_private :LsaFreeMemory, [:pvoid], :ntstatus # https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsantstatustowinerror # ULONG LsaNtStatusToWinError( # NTSTATUS Status # ); ffi_lib :advapi32 attach_function_private :LsaNtStatusToWinError, [:ntstatus], :ulong end puppetlabs-puppet-789f600/lib/puppet/util/yaml.rb000066400000000000000000000045261470131746300220350ustar00rootroot00000000000000# frozen_string_literal: true require 'yaml' module Puppet::Util::Yaml YamlLoadExceptions = [::StandardError, ::Psych::Exception] class YamlLoadError < Puppet::Error; end # Safely load the content as YAML. By default only the following # classes can be deserialized: # # * TrueClass # * FalseClass # * NilClass # * Numeric # * String # * Array # * Hash # # Attempting to deserialize other classes will raise an YamlLoadError # exception unless they are specified in the array of *allowed_classes*. # @param [String] yaml The yaml content to parse. # @param [Array] allowed_classes Additional list of classes that can be deserialized. # @param [String] filename The filename to load from, used if an exception is raised. # @raise [YamlLoadException] If deserialization fails. # @return The parsed YAML, which can be Hash, Array or scalar types. def self.safe_load(yaml, allowed_classes = [], filename = nil) if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0') data = YAML.safe_load(yaml, permitted_classes: allowed_classes, aliases: true, filename: filename) else data = YAML.safe_load(yaml, allowed_classes, [], true, filename) end data = false if data.nil? data rescue ::Psych::DisallowedClass => detail path = filename ? "(#{filename})" : "()" raise YamlLoadError.new("#{path}: #{detail.message}", detail) rescue *YamlLoadExceptions => detail raise YamlLoadError.new(detail.message, detail) end # Safely load the content from a file as YAML. # # @see Puppet::Util::Yaml.safe_load def self.safe_load_file(filename, allowed_classes = []) yaml = Puppet::FileSystem.read(filename, :encoding => 'bom|utf-8') safe_load(yaml, allowed_classes, filename) end # Safely load the content from a file as YAML if # contents are in valid format. This method does not # raise error but returns `nil` when invalid file is # given. def self.safe_load_file_if_valid(filename, allowed_classes = []) safe_load_file(filename, allowed_classes) rescue YamlLoadError, ArgumentError, Errno::ENOENT => detail Puppet.debug("Could not retrieve YAML content from '#{filename}': #{detail.message}") nil end def self.dump(structure, filename) Puppet::FileSystem.replace_file(filename, 0o660) do |fh| YAML.dump(structure, fh) end end end puppetlabs-puppet-789f600/lib/puppet/vendor.rb000066400000000000000000000037761470131746300214210ustar00rootroot00000000000000# frozen_string_literal: true module Puppet # Simple module to manage vendored code. # # To vendor a library: # # * Download its whole git repo or untar into `lib/puppet/vendor/` # * Create a vendor/puppetload_libraryname.rb file to add its libdir into the $:. # (Look at existing load_xxx files, they should all follow the same pattern). # * Add a /PUPPET_README.md file describing what the library is for # and where it comes from. # * To load the vendored lib upfront, add a `require ''`line to # `vendor/require_vendored.rb`. # * To load the vendored lib on demand, add a comment to `vendor/require_vendored.rb` # to make it clear it should not be loaded upfront. # # At runtime, the #load_vendored method should be called. It will ensure # all vendored libraries are added to the global `$:` path, and # will then call execute the up-front loading specified in `vendor/require_vendored.rb`. # # The intention is to not change vendored libraries and to eventually # make adding them in optional so that distros can simply adjust their # packaging to exclude this directory and the various load_xxx.rb scripts # if they wish to install these gems as native packages. # class Vendor class << self # @api private def vendor_dir File.join([File.dirname(File.expand_path(__FILE__)), "vendor"]) end # @api private def load_entry(entry) Puppet.debug("Loading vendored #{entry}") load "#{vendor_dir}/#{entry}" end # @api private def require_libs require_relative 'vendor/require_vendored' end # Configures the path for all vendored libraries and loads required libraries. # (This is the entry point for loading vendored libraries). # def load_vendored Dir.entries(vendor_dir).each do |entry| if entry =~ /load_(\w+?)\.rb$/ load_entry entry end end require_libs end end end end puppetlabs-puppet-789f600/lib/puppet/vendor/000077500000000000000000000000001470131746300210575ustar00rootroot00000000000000puppetlabs-puppet-789f600/lib/puppet/vendor/require_vendored.rb000066400000000000000000000003241470131746300247450ustar00rootroot00000000000000# This adds upfront requirements on vendored code found under lib/vendor/x # Add one requirement per vendored package (or a comment if it is loaded on demand). # The vendored library 'rgen' is loaded on demand. puppetlabs-puppet-789f600/lib/puppet/version.rb000066400000000000000000000072431470131746300216020ustar00rootroot00000000000000# frozen_string_literal: true # The version method and constant are isolated in puppet/version.rb so that a # simple `require 'puppet/version'` allows a rubygems gemspec or bundler # Gemfile to get the Puppet version of the gem install. # # The version can be set programmatically because we want to allow the # Raketasks and such to set the version based on the output of `git describe` module Puppet PUPPETVERSION = '8.10.0' ## # version is a public API method intended to always provide a fast and # lightweight way to determine the version of Puppet. # # The intent is that software external to Puppet be able to determine the # Puppet version with no side-effects. The expected use is: # # require 'puppet/version' # version = Puppet.version # # This function has the following ordering precedence. This precedence list # is designed to facilitate automated packaging tasks by simply writing to # the VERSION file in the same directory as this source file. # # 1. If a version has been explicitly assigned using the Puppet.version= # method, return that version. # 2. If there is a VERSION file, read the contents, trim any # trailing whitespace, and return that version string. # 3. Return the value of the Puppet::PUPPETVERSION constant hard-coded into # the source code. # # If there is no VERSION file, the method must return the version string of # the nearest parent version that is an officially released version. That is # to say, if a branch named 3.1.x contains 25 patches on top of the most # recent official release of 3.1.1, then the version method must return the # string "3.1.1" if no "VERSION" file is present. # # By design the version identifier is _not_ intended to vary during the life # a process. There is no guarantee provided that writing to the VERSION file # while a Puppet process is running will cause the version string to be # updated. On the contrary, the contents of the VERSION are cached to reduce # filesystem accesses. # # The VERSION file is intended to be used by package maintainers who may be # applying patches or otherwise changing the software version in a manner # that warrants a different software version identifier. The VERSION file is # intended to be managed and owned by the release process and packaging # related tasks, and as such should not reside in version control. The # PUPPETVERSION constant is intended to be version controlled in history. # # Ideally, this behavior will allow package maintainers to precisely specify # the version of the software they're packaging as in the following example: # # $ git describe --match "3.0.*" > lib/puppet/VERSION # $ ruby -r puppet -e 'puts Puppet.version' # 3.0.1-260-g9ca4e54 # # @api public # # @return [String] containing the puppet version, e.g. "3.0.1" def self.version version_file = File.join(File.dirname(__FILE__), 'VERSION') return @puppet_version if @puppet_version @puppet_version = read_version_file(version_file) || PUPPETVERSION end # @return [String] containing the puppet version to minor specificity, e.g. "3.0" def self.minor_version version.split('.')[0..1].join('.') end def self.version=(version) @puppet_version = version end ## # read_version_file reads the content of the "VERSION" file that lives in the # same directory as this source code file. # # @api private # # @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.exist?(path) File.read(path).chomp end end private_class_method :read_version_file end puppetlabs-puppet-789f600/lib/puppet/x509.rb000066400000000000000000000005101470131746300206100ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../puppet' require_relative '../puppet/ssl/openssl_loader' # Responsible for loading and saving certificates and private keys. # # @see Puppet::X509::CertProvider # @api private module Puppet::X509 require_relative 'x509/pem_store' require_relative 'x509/cert_provider' end puppetlabs-puppet-789f600/lib/puppet/x509/000077500000000000000000000000001470131746300202675ustar00rootroot00000000000000puppetlabs-puppet-789f600/lib/puppet/x509/cert_provider.rb000066400000000000000000000334241470131746300234710ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/x509' # Class for loading and saving cert related objects. By default the provider # loads and saves based on puppet's default settings, such as `Puppet[:localcacert]`. # The providers sets the permissions on files it saves, such as the private key. # All of the `load_*` methods take an optional `required` parameter. If an object # doesn't exist, then by default the provider returns `nil`. However, if the # `required` parameter is true, then an exception will be raised instead. # # @api private class Puppet::X509::CertProvider include Puppet::X509::PemStore # Only allow printing ascii characters, excluding / VALID_CERTNAME = /\A[ -.0-~]+\Z/ CERT_DELIMITERS = /-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----/m CRL_DELIMITERS = /-----BEGIN X509 CRL-----.*?-----END X509 CRL-----/m def initialize(capath: Puppet[:localcacert], crlpath: Puppet[:hostcrl], privatekeydir: Puppet[:privatekeydir], certdir: Puppet[:certdir], requestdir: Puppet[:requestdir], hostprivkey: Puppet.settings.set_by_config?(:hostprivkey) ? Puppet[:hostprivkey] : nil, hostcert: Puppet.settings.set_by_config?(:hostcert) ? Puppet[:hostcert] : nil) @capath = capath @crlpath = crlpath @privatekeydir = privatekeydir @certdir = certdir @requestdir = requestdir @hostprivkey = hostprivkey @hostcert = hostcert end # Save `certs` to the configured `capath`. # # @param certs [Array] Array of CA certs to save # @raise [Puppet::Error] if the certs cannot be saved # # @api private def save_cacerts(certs) save_pem(certs.map(&:to_pem).join, @capath, **permissions_for_setting(:localcacert)) rescue SystemCallError => e raise Puppet::Error.new(_("Failed to save CA certificates to '%{capath}'") % { capath: @capath }, e) end # Load CA certs from the configured `capath`. # # @param required [Boolean] If true, raise if they are missing # @return (see #load_cacerts_from_pem) # @raise (see #load_cacerts_from_pem) # @raise [Puppet::Error] if the certs cannot be loaded # # @api private def load_cacerts(required: false) pem = load_pem(@capath) if !pem && required raise Puppet::Error, _("The CA certificates are missing from '%{path}'") % { path: @capath } end pem ? load_cacerts_from_pem(pem) : nil rescue SystemCallError => e raise Puppet::Error.new(_("Failed to load CA certificates from '%{capath}'") % { capath: @capath }, e) end # Load PEM encoded CA certificates. # # @param pem [String] PEM encoded certificate(s) # @return [Array] Array of CA certs # @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert # # @api private def load_cacerts_from_pem(pem) # TRANSLATORS 'PEM' is an acronym and shouldn't be translated raise OpenSSL::X509::CertificateError, _("Failed to parse CA certificates as PEM") if pem !~ CERT_DELIMITERS pem.scan(CERT_DELIMITERS).map do |text| OpenSSL::X509::Certificate.new(text) end end # Save `crls` to the configured `crlpath`. # # @param crls [Array] Array of CRLs to save # @raise [Puppet::Error] if the CRLs cannot be saved # # @api private def save_crls(crls) save_pem(crls.map(&:to_pem).join, @crlpath, **permissions_for_setting(:hostcrl)) rescue SystemCallError => e raise Puppet::Error.new(_("Failed to save CRLs to '%{crlpath}'") % { crlpath: @crlpath }, e) end # Load CRLs from the configured `crlpath` path. # # @param required [Boolean] If true, raise if they are missing # @return (see #load_crls_from_pem) # @raise (see #load_crls_from_pem) # @raise [Puppet::Error] if the CRLs cannot be loaded # # @api private def load_crls(required: false) pem = load_pem(@crlpath) if !pem && required raise Puppet::Error, _("The CRL is missing from '%{path}'") % { path: @crlpath } end pem ? load_crls_from_pem(pem) : nil rescue SystemCallError => e raise Puppet::Error.new(_("Failed to load CRLs from '%{crlpath}'") % { crlpath: @crlpath }, e) end # Load PEM encoded CRL(s). # # @param pem [String] PEM encoded CRL(s) # @return [Array] Array of CRLs # @raise [OpenSSL::X509::CRLError] The `pem` text does not contain a valid CRL # # @api private def load_crls_from_pem(pem) # TRANSLATORS 'PEM' is an acronym and shouldn't be translated raise OpenSSL::X509::CRLError, _("Failed to parse CRLs as PEM") if pem !~ CRL_DELIMITERS pem.scan(CRL_DELIMITERS).map do |text| OpenSSL::X509::CRL.new(text) end end # Return the time when the CRL was last updated. # # @return [Time, nil] Time when the CRL was last updated, or nil if we don't # have a CRL # # @api private def crl_last_update stat = Puppet::FileSystem.stat(@crlpath) Time.at(stat.mtime) rescue Errno::ENOENT nil end # Set the CRL last updated time. # # @param time [Time] The last updated time # # @api private def crl_last_update=(time) Puppet::FileSystem.touch(@crlpath, mtime: time) end # Return the time when the CA bundle was last updated. # # @return [Time, nil] Time when the CA bundle was last updated, or nil if we don't # have a CA bundle # # @api private def ca_last_update stat = Puppet::FileSystem.stat(@capath) Time.at(stat.mtime) rescue Errno::ENOENT nil end # Set the CA bundle last updated time. # # @param time [Time] The last updated time # # @api private def ca_last_update=(time) Puppet::FileSystem.touch(@capath, mtime: time) end # Save named private key in the configured `privatekeydir`. For # historical reasons, names are case insensitive. # # @param name [String] The private key identity # @param key [OpenSSL::PKey::RSA] private key # @param password [String, nil] If non-nil, derive an encryption key # from the password, and use that to encrypt the private key. If nil, # save the private key unencrypted. # @raise [Puppet::Error] if the private key cannot be saved # # @api private def save_private_key(name, key, password: nil) pem = if password cipher = OpenSSL::Cipher.new('aes-128-cbc') key.export(cipher, password) else key.to_pem end path = @hostprivkey || to_path(@privatekeydir, name) save_pem(pem, path, **permissions_for_setting(:hostprivkey)) rescue SystemCallError => e raise Puppet::Error.new(_("Failed to save private key for '%{name}'") % { name: name }, e) end # Load a private key from the configured `privatekeydir`. For # historical reasons, names are case-insensitive. # # @param name [String] The private key identity # @param required [Boolean] If true, raise if it is missing # @param password [String, nil] If the private key is encrypted, decrypt # it using the password. If the key is encrypted, but a password is # not specified, then the key cannot be loaded. # @return (see #load_private_key_from_pem) # @raise (see #load_private_key_from_pem) # @raise [Puppet::Error] if the private key cannot be loaded # # @api private def load_private_key(name, required: false, password: nil) path = @hostprivkey || to_path(@privatekeydir, name) pem = load_pem(path) if !pem && required raise Puppet::Error, _("The private key is missing from '%{path}'") % { path: path } end pem ? load_private_key_from_pem(pem, password: password) : nil rescue SystemCallError => e raise Puppet::Error.new(_("Failed to load private key for '%{name}'") % { name: name }, e) end # Load a PEM encoded private key. # # @param pem [String] PEM encoded private key # @param password [String, nil] If the private key is encrypted, decrypt # it using the password. If the key is encrypted, but a password is # not specified, then the key cannot be loaded. # @return [OpenSSL::PKey::RSA, OpenSSL::PKey::EC] The private key # @raise [OpenSSL::PKey::PKeyError] The `pem` text does not contain a valid key # # @api private def load_private_key_from_pem(pem, password: nil) # set a non-nil password to ensure openssl doesn't prompt password ||= '' OpenSSL::PKey.read(pem, password) end # Load the private key password. # # @return [String, nil] The private key password as a binary string or nil # if there is none. # # @api private def load_private_key_password Puppet::FileSystem.read(Puppet[:passfile], :encoding => Encoding::BINARY) rescue Errno::ENOENT nil end # Save a named client cert to the configured `certdir`. # # @param name [String] The client cert identity # @param cert [OpenSSL::X509::Certificate] The cert to save # @raise [Puppet::Error] if the client cert cannot be saved # # @api private def save_client_cert(name, cert) path = @hostcert || to_path(@certdir, name) save_pem(cert.to_pem, path, **permissions_for_setting(:hostcert)) rescue SystemCallError => e raise Puppet::Error.new(_("Failed to save client certificate for '%{name}'") % { name: name }, e) end # Load a named client cert from the configured `certdir`. # # @param name [String] The client cert identity # @param required [Boolean] If true, raise it is missing # @return (see #load_request_from_pem) # @raise (see #load_client_cert_from_pem) # @raise [Puppet::Error] if the client cert cannot be loaded # # @api private def load_client_cert(name, required: false) path = @hostcert || to_path(@certdir, name) pem = load_pem(path) if !pem && required raise Puppet::Error, _("The client certificate is missing from '%{path}'") % { path: path } end pem ? load_client_cert_from_pem(pem) : nil rescue SystemCallError => e raise Puppet::Error.new(_("Failed to load client certificate for '%{name}'") % { name: name }, e) end # Load a PEM encoded certificate. # # @param pem [String] PEM encoded cert # @return [OpenSSL::X509::Certificate] the certificate # @raise [OpenSSL::X509::CertificateError] The `pem` text does not contain a valid cert # # @api private def load_client_cert_from_pem(pem) OpenSSL::X509::Certificate.new(pem) end # Create a certificate signing request (CSR). # # @param name [String] the request identity # @param private_key [OpenSSL::PKey::RSA] private key # @return [Puppet::X509::Request] The request # # @api private def create_request(name, private_key) options = {} if Puppet[:dns_alt_names] && Puppet[:dns_alt_names] != '' options[:dns_alt_names] = Puppet[:dns_alt_names] 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 # Adds auto-renew attribute to CSR if the agent supports auto-renewal of # certificates if Puppet[:hostcert_renewal_interval] && Puppet[:hostcert_renewal_interval] > 0 options[:csr_attributes] ||= {} options[:csr_attributes].merge!({ '1.3.6.1.4.1.34380.1.3.2' => 'true' }) end csr = Puppet::SSL::CertificateRequest.new(name) csr.generate(private_key, options) end # Save a certificate signing request (CSR) to the configured `requestdir`. # # @param name [String] the request identity # @param csr [OpenSSL::X509::Request] the request # @raise [Puppet::Error] if the cert request cannot be saved # # @api private def save_request(name, csr) path = to_path(@requestdir, name) save_pem(csr.to_pem, path, **permissions_for_setting(:hostcsr)) rescue SystemCallError => e raise Puppet::Error.new(_("Failed to save certificate request for '%{name}'") % { name: name }, e) end # Load a named certificate signing request (CSR) from the configured `requestdir`. # # @param name [String] The request identity # @return (see #load_request_from_pem) # @raise (see #load_request_from_pem) # @raise [Puppet::Error] if the cert request cannot be saved # # @api private def load_request(name) path = to_path(@requestdir, name) pem = load_pem(path) pem ? load_request_from_pem(pem) : nil rescue SystemCallError => e raise Puppet::Error.new(_("Failed to load certificate request for '%{name}'") % { name: name }, e) end # Delete a named certificate signing request (CSR) from the configured `requestdir`. # # @param name [String] The request identity # @return [Boolean] true if the CSR was deleted # # @api private def delete_request(name) path = to_path(@requestdir, name) delete_pem(path) rescue SystemCallError => e raise Puppet::Error.new(_("Failed to delete certificate request for '%{name}'") % { name: name }, e) end # Load a PEM encoded certificate signing request (CSR). # # @param pem [String] PEM encoded request # @return [OpenSSL::X509::Request] the request # @raise [OpenSSL::X509::RequestError] The `pem` text does not contain a valid request # # @api private def load_request_from_pem(pem) OpenSSL::X509::Request.new(pem) end # Return the path to the cert related object (key, CSR, cert, etc). # # @param base [String] base directory # @param name [String] the name associated with the cert related object def to_path(base, name) raise _("Certname %{name} must not contain unprintable or non-ASCII characters") % { name: name.inspect } unless name =~ VALID_CERTNAME File.join(base, "#{name.downcase}.pem") end private def permissions_for_setting(name) setting = Puppet.settings.setting(name) perm = { mode: setting.mode.to_i(8) } if Puppet.features.root? && !Puppet::Util::Platform.windows? perm[:owner] = setting.owner perm[:group] = setting.group end perm end end puppetlabs-puppet-789f600/lib/puppet/x509/pem_store.rb000066400000000000000000000034121470131746300226110ustar00rootroot00000000000000# frozen_string_literal: true require_relative '../../puppet/x509' # Methods for managing PEM encoded files. While PEM encoded strings are # always ASCII, the files may contain user specified comments, so they are # UTF-8 encoded. # # @api private module Puppet::X509::PemStore # Load a pem encoded object. # # @param path [String] file path # @return [String, nil] The PEM encoded object or nil if the # path does not exist # @raise [Errno::EACCES] if permission is denied # @api private def load_pem(path) Puppet::FileSystem.read(path, encoding: 'UTF-8') rescue Errno::ENOENT nil end # Save pem encoded content to a file. If the file doesn't exist, it # will be created. Otherwise, the file will be overwritten. In both # cases the contents will be overwritten atomically so other # processes don't see a partially written file. # # @param pem [String] The PEM encoded object to write # @param path [String] The file path to write to # @raise [Errno::EACCES] if permission is denied # @raise [Errno::EPERM] if the operation cannot be completed # @api private def save_pem(pem, path, owner: nil, group: nil, mode: 0o644) Puppet::FileSystem.replace_file(path, mode) do |f| f.set_encoding('UTF-8') f.write(pem.encode('UTF-8')) end if !Puppet::Util::Platform.windows? && Puppet.features.root? && (owner || group) FileUtils.chown(owner, group, path) end end # Delete a pem encoded object, if it exists. # # @param path [String] The file path to delete # @return [Boolean] Returns true if the file was deleted, false otherwise # @raise [Errno::EACCES] if permission is denied # @api private def delete_pem(path) Puppet::FileSystem.unlink(path) true rescue Errno::ENOENT false end end puppetlabs-puppet-789f600/lib/puppet_pal.rb000066400000000000000000000005131470131746300207420ustar00rootroot00000000000000# frozen_string_literal: true # Puppet as a Library "PAL" # This is the entry point when using PAL as a standalone library. # # This requires all of puppet because 'settings' and many other things are still required in PAL. # Eventually that will not be the case. # require_relative 'puppet' require_relative 'puppet/pal/pal_api' puppetlabs-puppet-789f600/lib/puppet_x.rb000066400000000000000000000007721470131746300204440ustar00rootroot00000000000000# frozen_string_literal: true # The Puppet Extensions Module. # # Submodules of this module should be named after the publisher (e.g. # 'user' part of a Puppet Module name). You should also add the module # name to avoid further conflicts in the same namespace. So the # structure should be # `lib/puppet_x///`. # A Puppet Extension for 'puppetlabs-powershell' would live at # `lib/puppet_x/puppetlabs/powershell/`. # # @api public # module PuppetX end puppetlabs-puppet-789f600/locales/000077500000000000000000000000001470131746300171215ustar00rootroot00000000000000puppetlabs-puppet-789f600/locales/config.yaml000066400000000000000000000023501470131746300212520ustar00rootroot00000000000000--- # This is the project-specific configuration file for setting up # fast_gettext for your project. gettext: # This is used for the name of the .pot and .po files; they will be # called .pot project_name: 'puppet' # This is used in comments in the .pot and .po files to indicate what # project the files belong to and should be a little more descriptive than # package_name: Puppet automation framework # The locale that the default messages in the .pot file are in default_locale: en # The address for sending bug reports. bugs_address: https://tickets.puppetlabs.com # The holder of the copyright. copyright_holder: Puppet, Inc. # Patterns for +Dir.glob+ used to find all files that might contain # translatable content, relative to the project root directory source_files: - 'lib/**/*.rb' # Patterns for +Dir.glob+ used to find all files contained in # `source_files` that should be ignored when searching for translatable # content, relative to the project root directory exclude_files: - 'lib/puppet/pops/types/type_formatter.rb' # The semantic_puppet gem is temporarily vendored (PUP-7114), and it # handles its own translation. - 'lib/puppet/vendor/**/*.rb' puppetlabs-puppet-789f600/locales/en/000077500000000000000000000000001470131746300175235ustar00rootroot00000000000000puppetlabs-puppet-789f600/locales/en/puppet.po000066400000000000000000000012761470131746300214060ustar00rootroot00000000000000# English translations for Puppet automation framework package. # Copyright (C) 2017 Puppet, Inc. # This file is distributed under the same license as the Puppet automation framework package. # Automatically generated, 2017. # msgid "" msgstr "" "Project-Id-Version: Puppet automation framework 5.3.3-51-gc11ddbe\n" "\n" "Report-Msgid-Bugs-To: https://tickets.puppetlabs.com\n" "POT-Creation-Date: 2017-11-17 12:09+0000\n" "PO-Revision-Date: 2017-11-17 12:09+0000\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" puppetlabs-puppet-789f600/locales/puppet.pot000066400000000000000000011316671470131746300212010ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) 2024 Puppet, Inc. # This file is distributed under the same license as the Puppet automation framework package. # FIRST AUTHOR , 2024. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: Puppet automation framework 8.8.0-75-g7e19ae9bed\n" "\n" "Report-Msgid-Bugs-To: https://tickets.puppetlabs.com\n" "POT-Creation-Date: 2024-09-05 16:06+0000\n" "PO-Revision-Date: 2024-09-05 16:06+0000\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #. TRANSLATORS 'lookup' is a puppet function and should not be translated #: ../lib/hiera/puppet_function.rb:66 msgid "The function '%{class_name}' is deprecated in favor of using 'lookup'." msgstr "" #: ../lib/hiera/puppet_function.rb:67 msgid "See https://puppet.com/docs/puppet/%{minor_version}/deprecated_language.html" msgstr "" #: ../lib/hiera/scope.rb:49 ../lib/puppet/parser/scope.rb:535 ../lib/puppet/parser/scope.rb:539 msgid "Variable: %{name}" msgstr "" #: ../lib/hiera/scope.rb:50 ../lib/hiera/scope.rb:52 ../lib/puppet/parser/scope.rb:536 ../lib/puppet/parser/scope.rb:542 msgid "Undefined variable '%{name}'; %{reason}" msgstr "" #: ../lib/hiera_puppet.rb:16 msgid "Could not find data item %{key} in any Hiera data file and no default supplied" msgstr "" #: ../lib/hiera_puppet.rb:43 msgid "Please supply a parameter to perform a Hiera lookup" msgstr "" #: ../lib/hiera_puppet.rb:74 ../lib/puppet/indirector/hiera.rb:87 msgid "Config file %{hiera_config} not found, using Hiera defaults" msgstr "" #: ../lib/puppet.rb:132 msgid "Support for ruby version %{version} is deprecated and will be removed in a future release. See https://puppet.com/docs/puppet/latest/system_requirements.html for a list of supported ruby versions." msgstr "" #: ../lib/puppet.rb:218 msgid "The environmentpath setting cannot be empty or nil." msgstr "" #: ../lib/puppet/agent.rb:95 msgid "Run of %{client_class} already in progress; skipping (%{lockfile_path} exists)" msgstr "" #: ../lib/puppet/agent.rb:98 msgid "Exiting now because the maxwaitforlock timeout has been exceeded." msgstr "" #: ../lib/puppet/agent.rb:101 msgid "Another puppet instance is already running; --waitforlock flag used, waiting for running instance to finish." msgstr "" #: ../lib/puppet/agent.rb:102 ../lib/puppet/ssl/state_machine.rb:415 ../lib/puppet/ssl/state_machine.rb:446 msgid "Will try again in %{time} seconds." msgstr "" #: ../lib/puppet/agent.rb:107 msgid "Execution of %{client_class} did not complete within %{runtimeout} seconds and was terminated." msgstr "" #: ../lib/puppet/agent.rb:111 msgid "Could not run %{client_class}: %{detail}" msgstr "" #: ../lib/puppet/agent.rb:120 msgid "Shutdown/restart in progress (%{status}); skipping run" msgstr "" #: ../lib/puppet/agent.rb:138 msgid "puppet agent: applying configuration" msgstr "" #: ../lib/puppet/agent.rb:161 msgid "Could not create instance of %{client_class}: %{detail}" msgstr "" #: ../lib/puppet/agent.rb:176 msgid "" "Skipping run of %{client_class}; administratively disabled (Reason: '%{disable_message}');\n" "Use 'puppet agent --enable' to re-enable." msgstr "" #: ../lib/puppet/agent/disabler.rb:21 msgid "Enabling Puppet." msgstr "" #: ../lib/puppet/agent/disabler.rb:28 msgid "Disabling Puppet." msgstr "" #: ../lib/puppet/agent/locker.rb:28 msgid "Failed to acquire lock" msgstr "" #: ../lib/puppet/application.rb:233 msgid "Unable to find application '%{application_name}'. %{error}" msgstr "" #: ../lib/puppet/application.rb:254 msgid "Unable to load application class '%{class_name}' from file 'puppet/application/%{application_name}.rb'" msgstr "" #: ../lib/puppet/application.rb:307 msgid "Invalid environment mode '%{mode_name}'" msgstr "" #: ../lib/puppet/application.rb:407 msgid "Could not get application-specific default settings" msgstr "" #: ../lib/puppet/application.rb:413 msgid "Could not initialize" msgstr "" #: ../lib/puppet/application.rb:414 msgid "Could not parse application options" msgstr "" #: ../lib/puppet/application.rb:415 msgid "Could not prepare for execution" msgstr "" #: ../lib/puppet/application.rb:418 msgid "`puppet %{name}` is deprecated and will be removed in a future release." msgstr "" #: ../lib/puppet/application.rb:421 msgid "Could not configure routes from %{route_file}" msgstr "" #: ../lib/puppet/application.rb:422 msgid "Could not log runtime debug info" msgstr "" #: ../lib/puppet/application.rb:423 msgid "Could not run" msgstr "" #: ../lib/puppet/application.rb:432 msgid "No valid command or main" msgstr "" #: ../lib/puppet/application.rb:487 msgid "Could not set logdest to %{dest}." msgstr "" #: ../lib/puppet/application.rb:584 msgid "No help available for puppet %{app_name}" msgstr "" #: ../lib/puppet/application/agent.rb:25 ../lib/puppet/application/device.rb:25 msgid "Cancelling startup" msgstr "" #: ../lib/puppet/application/agent.rb:84 msgid "The puppet agent daemon" msgstr "" #: ../lib/puppet/application/agent.rb:425 msgid "Fingerprint asked but neither the certificate, nor the certificate request have been issued" msgstr "" #: ../lib/puppet/application/agent.rb:430 msgid "Failed to generate fingerprint: %{message}" msgstr "" #: ../lib/puppet/application/agent.rb:454 msgid "Starting Puppet client version %{version}" msgstr "" #: ../lib/puppet/application/agent.rb:470 msgid "The puppet agent command does not take parameters" msgstr "" #: ../lib/puppet/application/apply.rb:39 msgid "Apply Puppet manifests locally" msgstr "" #. TRANSLATORS "puppet apply" is a program command and should not be translated #. TRANSLATORS "puppet apply" is a program command and should not be translated #: ../lib/puppet/application/apply.rb:217 ../lib/puppet/application/apply.rb:344 msgid "For puppet apply" msgstr "" #: ../lib/puppet/application/apply.rb:225 msgid "%{file} is not readable" msgstr "" #: ../lib/puppet/application/apply.rb:311 ../lib/puppet/application/script.rb:241 msgid "Exiting" msgstr "" #: ../lib/puppet/application/apply.rb:354 msgid "Could not deserialize catalog from %{format}: %{detail}" msgstr "" #: ../lib/puppet/application/apply.rb:375 ../lib/puppet/application/script.rb:148 msgid "Could not find facts for %{node}" msgstr "" #: ../lib/puppet/application/apply.rb:387 ../lib/puppet/application/script.rb:156 msgid "Could not find node %{node}" msgstr "" #: ../lib/puppet/application/apply.rb:401 ../lib/puppet/application/script.rb:140 msgid "Could not find file %{manifest}" msgstr "" #: ../lib/puppet/application/apply.rb:403 msgid "Only one file can be applied per run. Skipping %{files}" msgstr "" #: ../lib/puppet/application/describe.rb:177 msgid "Display help about resource types" msgstr "" #: ../lib/puppet/application/device.rb:84 msgid "Manage remote network devices" msgstr "" #: ../lib/puppet/application/device.rb:232 msgid "resource command requires target" msgstr "" #: ../lib/puppet/application/device.rb:235 msgid "facts command requires target" msgstr "" #: ../lib/puppet/application/device.rb:239 msgid "missing argument: --target is required when using --apply" msgstr "" #: ../lib/puppet/application/device.rb:240 msgid "%{file} does not exist, cannot apply" msgstr "" #: ../lib/puppet/application/device.rb:258 msgid "Target device / certificate '%{target}' not found in %{config}" msgstr "" #: ../lib/puppet/application/device.rb:260 msgid "No device found in %{config}" msgstr "" #: ../lib/puppet/application/device.rb:317 msgid "retrieving resource: %{resource} from %{target} at %{scheme}%{url_host}%{port}%{url_path}" msgstr "" #: ../lib/puppet/application/device.rb:332 msgid "retrieving facts from %{target} at %{scheme}%{url_host}%{port}%{url_path}" msgstr "" #: ../lib/puppet/application/device.rb:355 msgid "starting applying configuration to %{target} at %{scheme}%{url_host}%{port}%{url_path}" msgstr "" #: ../lib/puppet/application/device.rb:390 ../lib/puppet/application/resource.rb:208 msgid "You must specify the type to display" msgstr "" #: ../lib/puppet/application/device.rb:391 ../lib/puppet/application/resource.rb:209 msgid "Could not find type %{type}" msgstr "" #: ../lib/puppet/application/doc.rb:30 msgid "Invalid output format %{arg}" msgstr "" #: ../lib/puppet/application/doc.rb:39 msgid "Invalid output mode %{arg}" msgstr "" #: ../lib/puppet/application/doc.rb:55 msgid "Generate Puppet references" msgstr "" #: ../lib/puppet/application/doc.rb:137 msgid "scanning: %{files}" msgstr "" #: ../lib/puppet/application/doc.rb:149 msgid "Could not generate documentation: %{detail}" msgstr "" #: ../lib/puppet/application/doc.rb:162 msgid "Could not find reference %{name}" msgstr "" #: ../lib/puppet/application/doc.rb:168 msgid "Could not generate reference %{name}: %{detail}" msgstr "" #: ../lib/puppet/application/face_base.rb:36 msgid "I don't know how to render '%{format}'" msgstr "" #: ../lib/puppet/application/face_base.rb:58 msgid "Cancelling Face" msgstr "" #: ../lib/puppet/application/face_base.rb:137 msgid "'%{face}' has no %{action} action. See `puppet help %{face}`." msgstr "" #: ../lib/puppet/application/face_base.rb:217 msgid "%{face} does not respond to action %{arg}" msgstr "" #: ../lib/puppet/application/face_base.rb:250 msgid "puppet %{face} %{action} takes %{arg_count} argument, but you gave %{given_count}" msgid_plural "puppet %{face} %{action} takes %{arg_count} arguments, but you gave %{given_count}" msgstr[0] "" msgstr[1] "" #: ../lib/puppet/application/face_base.rb:255 msgid "'puppet %{face}' is deprecated and will be removed in a future release" msgstr "" #: ../lib/puppet/application/face_base.rb:273 msgid "Try 'puppet help %{face} %{action}' for usage" msgstr "" #: ../lib/puppet/application/filebucket.rb:19 msgid "Store and retrieve files in a filebucket" msgstr "" #: ../lib/puppet/application/filebucket.rb:230 msgid "You must specify a file to back up" msgstr "" #: ../lib/puppet/application/filebucket.rb:234 msgid "%{file}: no such file" msgstr "" #: ../lib/puppet/application/filebucket.rb:238 msgid "%{file}: cannot read file" msgstr "" #: ../lib/puppet/application/filebucket.rb:260 ../lib/puppet/application/filebucket.rb:284 msgid "Need exactly two arguments: filebucket diff " msgstr "" #: ../lib/puppet/application/filebucket.rb:281 msgid "Comparing %{checksum_a} %{checksum_b} %{file_a} %{file_b}" msgstr "" #: ../lib/puppet/application/filebucket.rb:295 msgid "Cancelling" msgstr "" #: ../lib/puppet/application/lookup.rb:10 msgid "Run 'puppet lookup --help' for more details" msgstr "" #: ../lib/puppet/application/lookup.rb:104 msgid "Interactive Hiera lookup" msgstr "" #: ../lib/puppet/application/lookup.rb:277 msgid "" "The options %{deep_merge_opts} are only available with '--merge deep'\n" "%{run_help}" msgstr "" #: ../lib/puppet/application/lookup.rb:288 msgid "" "The --merge option only accepts %{strategies}, or %{last_strategy}\n" "%{run_help}" msgstr "" #: ../lib/puppet/application/lookup.rb:313 msgid "No keys were given to lookup." msgstr "" #: ../lib/puppet/application/lookup.rb:321 msgid "Unknown rendering format '%{format}'" msgstr "" #: ../lib/puppet/application/lookup.rb:362 msgid "Incorrectly formatted data in %{fact_file} given via the --facts flag (only accepts yaml and json files)" msgstr "" #: ../lib/puppet/application/lookup.rb:367 msgid "When overriding any of the %{trusted_facts_list} facts with %{fact_file} given via the --facts flag, they must all be overridden." msgstr "" #: ../lib/puppet/application/lookup.rb:396 msgid "CA is not available, the operation will continue without using trusted facts." msgstr "" #: ../lib/puppet/application/lookup.rb:426 msgid "No facts available for target node: %{node}" msgstr "" #: ../lib/puppet/application/resource.rb:38 msgid "The resource abstraction layer shell" msgstr "" #: ../lib/puppet/application/resource.rb:152 msgid "Editing with Yaml output is not supported" msgstr "" #: ../lib/puppet/application/resource.rb:216 msgid "Invalid parameter setting %{setting}" msgstr "" #: ../lib/puppet/application/resource.rb:251 msgid "Listing all file instances is not supported. Please specify a file or directory, e.g. puppet resource file /etc" msgstr "" #: ../lib/puppet/application/script.rb:21 msgid "Run a puppet manifests as a script without compiling a catalog" msgstr "" #: ../lib/puppet/application/script.rb:127 msgid "Bolt must be installed to use the script application" msgstr "" #: ../lib/puppet/application/script.rb:142 msgid "Only one file can be used per run. Skipping %{files}" msgstr "" #: ../lib/puppet/application/ssl.rb:10 msgid "Manage SSL keys and certificates for puppet SSL clients" msgstr "" #: ../lib/puppet/application/ssl.rb:112 msgid "An action must be specified." msgstr "" #: ../lib/puppet/application/ssl.rb:136 ../lib/puppet/application/ssl.rb:143 msgid "The certificate for '%{name}' has not yet been signed" msgstr "" #: ../lib/puppet/application/ssl.rb:167 msgid "Completed SSL initialization" msgstr "" #: ../lib/puppet/application/ssl.rb:171 msgid "Unknown action '%{action}'" msgstr "" #: ../lib/puppet/application/ssl.rb:192 msgid "Submitted certificate request for '%{name}' to %{url}" msgstr "" #: ../lib/puppet/application/ssl.rb:195 msgid "Could not submit certificate request for '%{name}' to %{url} due to a conflict on the server" msgstr "" #: ../lib/puppet/application/ssl.rb:197 ../lib/puppet/application/ssl.rb:200 msgid "Failed to submit certificate request: %{message}" msgstr "" #: ../lib/puppet/application/ssl.rb:212 msgid "Generated certificate request in '%{path}'" msgstr "" #: ../lib/puppet/application/ssl.rb:214 msgid "Failed to generate certificate request: %{message}" msgstr "" #: ../lib/puppet/application/ssl.rb:222 msgid "Downloading certificate '%{name}' from %{url}" msgstr "" #: ../lib/puppet/application/ssl.rb:226 msgid "Downloaded certificate '%{name}' with fingerprint %{fingerprint}" msgstr "" #: ../lib/puppet/application/ssl.rb:239 ../lib/puppet/application/ssl.rb:242 msgid "Failed to download certificate: %{message}" msgstr "" #: ../lib/puppet/application/ssl.rb:271 ../lib/puppet/application/ssl.rb:274 msgid "Failed to connect to the CA to determine if certificate %{certname} has been cleaned" msgstr "" #: ../lib/puppet/application/ssl.rb:301 msgid "Removed %{label} %{path}" msgstr "" #: ../lib/puppet/application/ssl.rb:318 ../lib/puppet/ssl/state_machine.rb:274 msgid "Creating a new EC SSL key for %{name} using curve %{curve}" msgstr "" #: ../lib/puppet/application/ssl.rb:321 msgid "Creating a new SSL key for %{name}" msgstr "" #: ../lib/puppet/configurer.rb:23 msgid "Puppet configuration client" msgstr "" #: ../lib/puppet/configurer.rb:46 msgid "Removing corrupt state file %{file}: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:51 msgid "Cannot remove %{file}: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:82 msgid "Using cached catalog from environment '%{environment}'" msgstr "" #: ../lib/puppet/configurer.rb:88 msgid "Not using cache on failed catalog" msgstr "" #: ../lib/puppet/configurer.rb:97 msgid "Not using cached catalog because its environment '%{catalog_env}' does not match '%{local_env}'" msgstr "" #: ../lib/puppet/configurer.rb:102 ../lib/puppet/configurer.rb:277 msgid "Using cached catalog from environment '%{catalog_env}'" msgstr "" #: ../lib/puppet/configurer.rb:135 msgid "The current total number of fact values: %{size} exceeds the fact values limit: %{max_size}" msgstr "" #: ../lib/puppet/configurer.rb:139 msgid "Fact %{name} with length: '%{length}' exceeds the length limit: %{limit}" msgstr "" #: ../lib/puppet/configurer.rb:143 msgid "The current number of top level facts: %{size} exceeds the top facts limit: %{max_size}" msgstr "" #: ../lib/puppet/configurer.rb:147 msgid "Fact value '%{value}' with the value length: '%{length}' exceeds the value length limit: %{max_length}" msgstr "" #: ../lib/puppet/configurer.rb:151 msgid "Payload with the current size of: '%{payload}' exceeds the payload size limit: %{max_size}" msgstr "" #: ../lib/puppet/configurer.rb:189 msgid "The size of the payload is %{payload}" msgstr "" #: ../lib/puppet/configurer.rb:219 msgid "The total number of facts registered is %{number_of_facts}" msgstr "" #: ../lib/puppet/configurer.rb:270 msgid "Could not retrieve catalog; skipping run" msgstr "" #: ../lib/puppet/configurer.rb:286 msgid "Applied catalog in %{seconds} seconds" msgstr "" #: ../lib/puppet/configurer.rb:320 msgid "Could not select a functional puppet server from server_list: '%{server_list}'" msgstr "" #. TRANSLATORS 'server_list' is the name of a setting and should not be translated #: ../lib/puppet/configurer.rb:334 msgid "Selected puppet server from the `server_list` setting: %{server}:%{port}" msgstr "" #: ../lib/puppet/configurer.rb:371 msgid "Local environment: '%{local_env}' doesn't match the environment of the cached catalog '%{catalog_env}', switching agent to '%{catalog_env}'." msgstr "" #: ../lib/puppet/configurer.rb:394 msgid "Environment not passed via CLI and no catalog was given, attempting to find out the last server-specified environment" msgstr "" #: ../lib/puppet/configurer.rb:398 msgid "Requesting environment from the server" msgstr "" #: ../lib/puppet/configurer.rb:408 msgid "Could not find a usable environment in the lastrunfile. Either the file does not exist, does not have the required keys, or the values of 'initial_environment' and 'converged_environment' are identical." msgstr "" #: ../lib/puppet/configurer.rb:412 msgid "Using environment '%{env}'" msgstr "" #: ../lib/puppet/configurer.rb:436 msgid "Not using catalog because its environment '%{catalog_env}' does not match agent specified environment '%{local_env}' and strict_environment_mode is set" msgstr "" #: ../lib/puppet/configurer.rb:447 msgid "Catalog environment didn't stabilize after %{tries} fetches, aborting run" msgstr "" #: ../lib/puppet/configurer.rb:450 msgid "Local environment: '%{local_env}' doesn't match server specified environment '%{catalog_env}', restarting agent run with environment '%{catalog_env}'" msgstr "" #: ../lib/puppet/configurer.rb:507 msgid "Failed to apply catalog: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:539 msgid "Environment '%{environment}' not found on server, aborting run." msgstr "" #: ../lib/puppet/configurer.rb:541 msgid "Environment '%{environment}' not found on server, skipping initial pluginsync." msgstr "" #: ../lib/puppet/configurer.rb:559 ../lib/puppet/http/resolver/server_list.rb:65 ../lib/puppet/http/resolver/server_list.rb:69 msgid "Puppet server %{host}:%{port} is unavailable: %{code} %{reason}" msgstr "" #. TRANSLATORS 'server_list' is the name of a setting and should not be translated #: ../lib/puppet/configurer.rb:563 ../lib/puppet/http/resolver/server_list.rb:74 ../lib/puppet/http/resolver/server_list.rb:77 msgid "Unable to connect to server from server_list setting: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:589 msgid "Successfully loaded last environment from the lastrunfile" msgstr "" #: ../lib/puppet/configurer.rb:593 msgid "Found last server-specified environment: %{environment}" msgstr "" #: ../lib/puppet/configurer.rb:596 msgid "Could not find last server-specified environment: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:616 msgid "Local environment: '%{local_env}' doesn't match server specified node environment '%{node_env}', switching agent to '%{node_env}'." msgstr "" #: ../lib/puppet/configurer.rb:624 msgid "Unable to fetch my node definition, but the agent run will continue:" msgstr "" #: ../lib/puppet/configurer.rb:643 ../lib/puppet/face/report.rb:48 msgid "Could not send report: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:652 msgid "Could not save last run local report: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:671 msgid "Uploading facts for %{node} to %{server}" msgstr "" #: ../lib/puppet/configurer.rb:680 msgid "Failed to submit facts: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:695 msgid "Could not run command from %{setting}: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:724 msgid "Could not retrieve catalog from cache: %{detail}" msgstr "" #: ../lib/puppet/configurer.rb:746 msgid "Could not retrieve catalog from remote server: %{detail}" msgstr "" #: ../lib/puppet/configurer/downloader.rb:11 msgid "Retrieving %{name}" msgstr "" #: ../lib/puppet/configurer/downloader.rb:23 msgid "Failed to retrieve %{name}: %{detail}" msgstr "" #: ../lib/puppet/configurer/downloader.rb:34 msgid "Could not retrieve %{name}: %{detail}" msgstr "" #: ../lib/puppet/configurer/fact_handler.rb:26 msgid "Could not retrieve local facts: %{detail}" msgstr "" #: ../lib/puppet/context.rb:79 msgid "Mark for '%{name}' already exists" msgstr "" #: ../lib/puppet/context.rb:94 msgid "Unknown mark '%{name}'" msgstr "" #: ../lib/puppet/context.rb:113 ../lib/puppet/context.rb:171 msgid "Unable to lookup '%{name}'" msgstr "" #: ../lib/puppet/context.rb:122 msgid "Attempted to pop, but already at root of the context stack." msgstr "" #: ../lib/puppet/context/trusted_information.rb:55 msgid "TrustedInformation expected a certificate, but none was given." msgstr "" #: ../lib/puppet/context/trusted_information.rb:106 ../lib/puppet/parser/scope.rb:848 msgid "Unsupported data type: '%{klass}'" msgstr "" #: ../lib/puppet/daemon.rb:28 msgid "Daemons must have an agent" msgstr "" #: ../lib/puppet/daemon.rb:84 msgid "Cannot reexec unless ARGV arguments are set" msgstr "" #: ../lib/puppet/datatypes.rb:135 msgid "Data Type Load Error for type '%{type_name}': %{message}" msgstr "" #: ../lib/puppet/datatypes.rb:156 msgid "a data type must have an interface" msgstr "" #: ../lib/puppet/datatypes.rb:197 msgid "a data type can only have one interface" msgstr "" #: ../lib/puppet/datatypes.rb:203 ../lib/puppet/datatypes.rb:209 msgid "a data type can only have one implementation" msgstr "" #: ../lib/puppet/defaults.rb:157 msgid "Cannot disable unrecognized warning types '%{invalid}'." msgstr "" #: ../lib/puppet/defaults.rb:158 msgid "Valid values are '%{values}'." msgstr "" #. TRANSLATORS 'data_binding_terminus' is a setting and should not be translated #: ../lib/puppet/defaults.rb:558 msgid "Setting 'data_binding_terminus' is deprecated." msgstr "" #. TRANSLATORS 'hiera' should not be translated #: ../lib/puppet/defaults.rb:560 msgid "Convert custom terminus to hiera 5 API." msgstr "" #. TRANSLATORS 'environment_data_provider' is a setting and should not be translated #: ../lib/puppet/defaults.rb:754 msgid "Setting 'environment_data_provider' is deprecated." msgstr "" #: ../lib/puppet/defaults.rb:844 msgid "Certificate names must be lower case" msgstr "" #: ../lib/puppet/defaults.rb:1103 ../lib/puppet/settings/enum_setting.rb:15 ../lib/puppet/settings/symbolic_enum_setting.rb:16 msgid "Invalid value '%{value}' for parameter %{name}. Allowed values are '%{allowed_values}'" msgstr "" #. TRANSLATORS 'pluginsync' is a setting and should not be translated #: ../lib/puppet/defaults.rb:2044 msgid "Setting 'pluginsync' is deprecated." msgstr "" #: ../lib/puppet/error.rb:80 msgid "Could not parse for environment %{environment}: %{message}" msgstr "" #: ../lib/puppet/error.rb:81 ../lib/puppet/parser/compiler.rb:42 msgid "%{message} on node %{node}" msgstr "" #: ../lib/puppet/face/catalog.rb:9 msgid "Compile, save, view, and convert catalogs." msgstr "" #: ../lib/puppet/face/catalog.rb:29 msgid "Retrieve the catalog for the node from which the command is run." msgstr "" #: ../lib/puppet/face/catalog.rb:32 msgid "Not implemented for the CLI; facts are collected internally." msgstr "" #: ../lib/puppet/face/catalog.rb:103 msgid "Compile a catalog." msgstr "" #: ../lib/puppet/face/catalog/select.rb:6 msgid "Retrieve a catalog and filter it for resources of a given type." msgstr "" #: ../lib/puppet/face/catalog/select.rb:7 msgid " " msgstr "" #: ../lib/puppet/face/catalog/select.rb:45 msgid "no matching resources found" msgstr "" #: ../lib/puppet/face/config.rb:9 ../lib/puppet/face/epp.rb:10 ../lib/puppet/face/facts.rb:8 ../lib/puppet/face/generate.rb:9 ../lib/puppet/face/help.rb:11 ../lib/puppet/face/module.rb:11 ../lib/puppet/face/node.rb:6 ../lib/puppet/face/parser.rb:8 ../lib/puppet/face/plugin.rb:8 ../lib/puppet/face/report.rb:7 ../lib/puppet/face/resource.rb:7 msgid "Apache 2 license; see COPYING" msgstr "" #: ../lib/puppet/face/config.rb:11 msgid "Interact with Puppet's settings." msgstr "" #: ../lib/puppet/face/config.rb:19 msgid "SECTION_NAME" msgstr "" #: ../lib/puppet/face/config.rb:21 msgid "The section of the configuration file to interact with." msgstr "" #: ../lib/puppet/face/config.rb:39 msgid "Examine Puppet's current settings." msgstr "" #: ../lib/puppet/face/config.rb:40 msgid "all | [ ...]" msgstr "" #: ../lib/puppet/face/config.rb:101 msgid "No section specified; defaulting to '%{section_name}'." msgstr "" #. TRANSLATORS '--section' is a command line option and should not be translated #: ../lib/puppet/face/config.rb:104 msgid "Set the config section by using the `--section` flag." msgstr "" #. TRANSLATORS `puppet config --section user print foo` is a command line example and should not be translated #: ../lib/puppet/face/config.rb:106 msgid "For example, `puppet config --section user print foo`." msgstr "" #: ../lib/puppet/face/config.rb:107 msgid "For more information, see https://puppet.com/docs/puppet/latest/configuration.html" msgstr "" #: ../lib/puppet/face/config.rb:114 msgid "Resolving settings from section '%{section_name}' in environment '%{environment_name}'" msgstr "" #: ../lib/puppet/face/config.rb:119 msgid "Set Puppet's settings." msgstr "" #: ../lib/puppet/face/config.rb:120 msgid "[setting_name] [setting_value]" msgstr "" #: ../lib/puppet/face/config.rb:187 msgid "Deleted setting from '%{section_name}': '%{setting_string}', and adding it to 'server' section" msgstr "" #: ../lib/puppet/face/config.rb:202 msgid "Delete a Puppet setting." msgstr "" #: ../lib/puppet/face/config.rb:203 msgid "" msgstr "" #: ../lib/puppet/face/config.rb:235 ../lib/puppet/face/config.rb:239 ../lib/puppet/face/config.rb:250 msgid "Deleted setting from '%{section_name}': '%{setting_string}'" msgstr "" #: ../lib/puppet/face/config.rb:253 msgid "No setting found in configuration file for section '%{section_name}' setting name '%{name}'" msgstr "" #. TRANSLATORS the 'puppet.conf' is a specific file and should not be translated #: ../lib/puppet/face/config.rb:261 msgid "The puppet.conf file does not exist %{puppet_conf}" msgstr "" #: ../lib/puppet/face/epp.rb:12 msgid "Interact directly with the EPP template parser/renderer." msgstr "" #: ../lib/puppet/face/epp.rb:15 msgid "Validate the syntax of one or more EPP templates." msgstr "" #: ../lib/puppet/face/epp.rb:16 msgid "[